Merge pull request #35 from TomPallister/develop

Merge load balancing reroutes
This commit is contained in:
geffzhang 2018-02-02 08:50:40 +08:00 committed by GitHub
commit 3e2c410626
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
440 changed files with 29620 additions and 28107 deletions

View File

@ -22,13 +22,17 @@ We then map this to a ReRoute in the configuration e.g.
.. code-block:: json .. code-block:: json
"ReRoutes": [{ "ReRoutes": [{
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 51876,
}
],
"DownstreamPathTemplate": "/", "DownstreamPathTemplate": "/",
"UpstreamPathTemplate": "/", "UpstreamPathTemplate": "/",
"UpstreamHttpMethod": ["Post"], "UpstreamHttpMethod": ["Post"],
"ReRouteIsCaseSensitive": false, "ReRouteIsCaseSensitive": false,
"DownstreamScheme": "http", "DownstreamScheme": "http",
"DownstreamHost": "localhost",
"DownstreamPort": 51876,
"AuthenticationOptions": { "AuthenticationOptions": {
"AuthenticationProviderKey": "TestKey", "AuthenticationProviderKey": "TestKey",
"AllowedScopes": [] "AllowedScopes": []
@ -67,13 +71,17 @@ Then map the authentication provider key to a ReRoute in your configuration e.g.
.. code-block:: json .. code-block:: json
"ReRoutes": [{ "ReRoutes": [{
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 51876,
}
],
"DownstreamPathTemplate": "/", "DownstreamPathTemplate": "/",
"UpstreamPathTemplate": "/", "UpstreamPathTemplate": "/",
"UpstreamHttpMethod": ["Post"], "UpstreamHttpMethod": ["Post"],
"ReRouteIsCaseSensitive": false, "ReRouteIsCaseSensitive": false,
"DownstreamScheme": "http", "DownstreamScheme": "http",
"DownstreamHost": "localhost",
"DownstreamPort": 51876,
"AuthenticationOptions": { "AuthenticationOptions": {
"AuthenticationProviderKey": "TestKey", "AuthenticationProviderKey": "TestKey",
"AllowedScopes": [] "AllowedScopes": []
@ -111,13 +119,17 @@ Then map the authentication provider key to a ReRoute in your configuration e.g.
.. code-block:: json .. code-block:: json
"ReRoutes": [{ "ReRoutes": [{
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 51876,
}
],
"DownstreamPathTemplate": "/", "DownstreamPathTemplate": "/",
"UpstreamPathTemplate": "/", "UpstreamPathTemplate": "/",
"UpstreamHttpMethod": ["Post"], "UpstreamHttpMethod": ["Post"],
"ReRouteIsCaseSensitive": false, "ReRouteIsCaseSensitive": false,
"DownstreamScheme": "http", "DownstreamScheme": "http",
"DownstreamHost": "localhost",
"DownstreamPort": 51876,
"AuthenticationOptions": { "AuthenticationOptions": {
"AuthenticationProviderKey": "TestKey", "AuthenticationProviderKey": "TestKey",
"AllowedScopes": [] "AllowedScopes": []

View File

@ -14,15 +14,6 @@ if you don't want to manage lots of ReRoute specific settings.
"GlobalConfiguration": {} "GlobalConfiguration": {}
} }
Follow Redirects / Use CookieContainer
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Use HttpHandlerOptions in ReRoute configuration to set up HttpHandler behavior:
- _AllowAutoRedirect_ is a value that indicates whether the request should follow redirection responses.
Set it true if the request should automatically follow redirection responses from the Downstream resource; otherwise false. The default value is true.
- _UseCookieContainer_ is a value that indicates whether the handler uses the CookieContainer property to store server cookies and uses these cookies when sending requests.
The default value is true.
Here is an example ReRoute configuration, You don't need to set all of these things but this is everything that is available at the moment: Here is an example ReRoute configuration, You don't need to set all of these things but this is everything that is available at the moment:
.. code-block:: json .. code-block:: json
@ -45,8 +36,12 @@ Here is an example ReRoute configuration, You don't need to set all of these thi
"ReRouteIsCaseSensitive": false, "ReRouteIsCaseSensitive": false,
"ServiceName": "", "ServiceName": "",
"DownstreamScheme": "http", "DownstreamScheme": "http",
"DownstreamHost": "localhost", "DownstreamHostAndPorts": [
"DownstreamPort": 51779, {
"Host": "localhost",
"Port": 51876,
}
],
"QoSOptions": { "QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 0, "ExceptionsAllowedBeforeBreaking": 0,
"DurationOfBreak": 0, "DurationOfBreak": 0,
@ -73,6 +68,15 @@ Here is an example ReRoute configuration, You don't need to set all of these thi
More information on how to use these options is below.. More information on how to use these options is below..
Follow Redirects / Use CookieContainer
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Use HttpHandlerOptions in ReRoute configuration to set up HttpHandler behavior:
- _AllowAutoRedirect_ is a value that indicates whether the request should follow redirection responses.
Set it true if the request should automatically follow redirection responses from the Downstream resource; otherwise false. The default value is true.
- _UseCookieContainer_ is a value that indicates whether the handler uses the CookieContainer property to store server cookies and uses these cookies when sending requests.
The default value is true.
Store configuration in consul Store configuration in consul
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -39,9 +39,10 @@ Add the following to a ReRoute in configuration.json in order to replace http://
Placeholders Placeholders
^^^^^^^^^^^^ ^^^^^^^^^^^^
Ocelot allows placeholders that can be used in header transformation. At the moment there is only one placeholder. Ocelot allows placeholders that can be used in header transformation.
{BaseUrl} - This will use Ocelot's base url e.g. http://localhost:5000 as its value. {BaseUrl} - This will use Ocelot's base url e.g. http://localhost:5000 as its value.
{DownstreamBaseUrl} - This will use the downstream services base url e.g. http://localhost:5000 as its value. This only works for DownstreamHeaderTransform at the moment.
Handling 302 Redirects Handling 302 Redirects
^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^
@ -67,4 +68,30 @@ or you could use the BaseUrl placeholder.
"AllowAutoRedirect": false, "AllowAutoRedirect": false,
}, },
Ocelot will not try and replace the location header returned by the downstream service with its own URL. finally if you are using a load balancer with Ocelot you will get multiple downstream base urls so the above would not work. In this case you can do the following.
.. code-block:: json
"DownstreamHeaderTransform": {
"Location": "{DownstreamBaseUrl}, {BaseUrl}"
},
"HttpHandlerOptions": {
"AllowAutoRedirect": false,
},
Future
^^^^^^
Ideally this feature would be able to support the fact that a header can have multiple values. At the moment it just assumes one.
It would also be nice if it could multi find and replace e.g.
.. code-block:: json
"DownstreamHeaderTransform": {
"Location": "[{one,one},{two,two}"
},
"HttpHandlerOptions": {
"AllowAutoRedirect": false,
},
If anyone wants to have a go at this please help yourself!!

View File

@ -0,0 +1,60 @@
Load Balancer
=============
Ocelot can load balance across available downstream services for each ReRoute. This means you can scale your downstream services and Ocelot can use them effectively.
The type of load balancer available are:
LeastConnection - tracks which services are dealing with requests and sends new requests to service with least existing requests. The algorythm state is not distributed across a cluster of Ocelot's.
RoundRobin - loops through available services and sends requests. The algorythm state is not distributed across a cluster of Ocelot's.
NoLoadBalancer - takes the first available service from config or service discovery.
You must choose in your configuration which load balancer to use.
Configuration
^^^^^^^^^^^^^
The following shows how to set up multiple downstream services for a ReRoute using configuration.json and then select the LeadConnection load balancer. This is the simplest way to get load balancing set up.
.. code-block:: json
{
"DownstreamPathTemplate": "/api/posts/{postId}",
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "10.0.1.10",
"Port": 5000,
},
{
"Host": "10.0.1.11",
"Port": 5000,
}
],
"UpstreamPathTemplate": "/posts/{postId}",
"LoadBalancer": "LeastConnection",
"UpstreamHttpMethod": [ "Put", "Delete" ]
}
Service Discovery
^^^^^^^^^^^^^^^^^
The following shows how to set up a ReRoute using service discovery then select the LeadConnection load balancer.
.. code-block:: json
{
"DownstreamPathTemplate": "/api/posts/{postId}",
"DownstreamScheme": "https",
"UpstreamPathTemplate": "/posts/{postId}",
"UpstreamHttpMethod": [ "Put" ],
"ServiceName": "product",
"LoadBalancer": "LeastConnection",
"UseServiceDiscovery": true
}
When this is set up Ocelot will lookup the downstream host and port from the service discover provider and load balance requests across any available services. If you add and remove services from the
service discovery provider (consul) then Ocelot should respect this and stop calling services that have been removed and start calling services that have been added.

View File

@ -23,20 +23,26 @@ the following.
{ {
"DownstreamPathTemplate": "/api/posts/{postId}", "DownstreamPathTemplate": "/api/posts/{postId}",
"DownstreamScheme": "https", "DownstreamScheme": "https",
"DownstreamPort": 80, "DownstreamHostAndPorts": [
"DownstreamHost":"localhost", {
"Host": "localhost",
"Port": 80,
}
],
"UpstreamPathTemplate": "/posts/{postId}", "UpstreamPathTemplate": "/posts/{postId}",
"UpstreamHttpMethod": [ "Put", "Delete" ] "UpstreamHttpMethod": [ "Put", "Delete" ]
} }
The DownstreamPathTemplate, Scheme, Port and Host make the URL that this request will be forwarded to. The DownstreamPathTemplate, Scheme and DownstreamHostAndPorts make the URL that this request will be forwarded to.
The UpstreamPathTemplate is the URL that Ocelot will use to identity which
DownstreamPathTemplate to use for a given request. Finally the UpstreamHttpMethod is used so DownstreamHostAndPorts is an array that contains the host and port of any downstream services that you wish to forward requests to. Usually this will just contain one entry but sometimes you might want to load balance
requests to your downstream services and Ocelot let's you add more than one entry and then select a load balancer.
The UpstreamPathTemplate is the URL that Ocelot will use to identity which DownstreamPathTemplate to use for a given request. Finally the UpstreamHttpMethod is used so
Ocelot can distinguish between requests to the same URL and is obviously needed to work :) Ocelot can distinguish between requests to the same URL and is obviously needed to work :)
You can set a specific list of HTTP Methods or set an empty list to allow any of them. In Ocelot you can add placeholders for variables to your Templates in the form of {something}. You can set a specific list of HTTP Methods or set an empty list to allow any of them. In Ocelot you can add placeholders for variables to your Templates in the form of {something}.
The placeholder needs to be in both the DownstreamPathTemplate and UpstreamPathTemplate. If it is The placeholder needs to be in both the DownstreamPathTemplate and UpstreamPathTemplate. If it is Ocelot will attempt to replace the placeholder with the correct variable value from the Upstream URL when the request comes in.
Ocelot will attempt to replace the placeholder with the correct variable value from the
Upstream URL when the request comes in.
You can also do a catch all type of ReRoute e.g. You can also do a catch all type of ReRoute e.g.
@ -45,8 +51,12 @@ You can also do a catch all type of ReRoute e.g.
{ {
"DownstreamPathTemplate": "/api/{everything}", "DownstreamPathTemplate": "/api/{everything}",
"DownstreamScheme": "https", "DownstreamScheme": "https",
"DownstreamPort": 80, "DownstreamHostAndPorts": [
"DownstreamHost":"localhost", {
"Host": "localhost",
"Port": 80,
}
],
"UpstreamPathTemplate": "/{everything}", "UpstreamPathTemplate": "/{everything}",
"UpstreamHttpMethod": [ "Get", "Post" ] "UpstreamHttpMethod": [ "Get", "Post" ]
} }
@ -74,8 +84,12 @@ Ocelot's routing also supports a catch all style routing where the user can spec
{ {
"DownstreamPathTemplate": "/{url}", "DownstreamPathTemplate": "/{url}",
"DownstreamScheme": "https", "DownstreamScheme": "https",
"DownstreamPort": 80, "DownstreamHostAndPorts": [
"DownstreamHost":"localhost", {
"Host": "localhost",
"Port": 80,
}
],
"UpstreamPathTemplate": "/{url}", "UpstreamPathTemplate": "/{url}",
"UpstreamHttpMethod": [ "Get" ] "UpstreamHttpMethod": [ "Get" ]
} }
@ -87,8 +101,12 @@ The catch all has a lower priority than any other ReRoute. If you also have the
{ {
"DownstreamPathTemplate": "/", "DownstreamPathTemplate": "/",
"DownstreamScheme": "https", "DownstreamScheme": "https",
"DownstreamPort": 80, "DownstreamHostAndPorts": [
"DownstreamHost":"10.0.10.1", {
"Host": "10.0.10.1",
"Port": 80,
}
],
"UpstreamPathTemplate": "/", "UpstreamPathTemplate": "/",
"UpstreamHttpMethod": [ "Get" ] "UpstreamHttpMethod": [ "Get" ]
} }

View File

@ -32,7 +32,7 @@ and LeastConnection algorithm you can use. If no load balancer is specified Ocel
"UpstreamHttpMethod": [ "Put" ], "UpstreamHttpMethod": [ "Put" ],
"ServiceName": "product", "ServiceName": "product",
"LoadBalancer": "LeastConnection", "LoadBalancer": "LeastConnection",
"UseServiceDiscovery": false "UseServiceDiscovery": true
} }
When this is set up Ocelot will lookup the downstream host and port from the service discover provider and load balance requests across any available services. When this is set up Ocelot will lookup the downstream host and port from the service discover provider and load balance requests across any available services.

View File

@ -32,6 +32,7 @@ Thanks for taking a look at the Ocelot documentation. Please use the left hand n
features/logging features/logging
features/requestid features/requestid
features/middlewareinjection features/middlewareinjection
features/loadbalancer
.. toctree:: .. toctree::
:maxdepth: 2 :maxdepth: 2

View File

@ -3,6 +3,7 @@ using System.Net.Http;
using Ocelot.Values; using Ocelot.Values;
using System.Linq; using System.Linq;
using Ocelot.Configuration.Creator; using Ocelot.Configuration.Creator;
using System;
namespace Ocelot.Configuration.Builder namespace Ocelot.Configuration.Builder
{ {
@ -24,20 +25,28 @@ namespace Ocelot.Configuration.Builder
private bool _isCached; private bool _isCached;
private CacheOptions _fileCacheOptions; private CacheOptions _fileCacheOptions;
private string _downstreamScheme; private string _downstreamScheme;
private string _downstreamHost;
private int _downstreamPort;
private string _loadBalancer; private string _loadBalancer;
private bool _useQos; private bool _useQos;
private QoSOptions _qosOptions; private QoSOptions _qosOptions;
private HttpHandlerOptions _httpHandlerOptions; private HttpHandlerOptions _httpHandlerOptions;
public bool _enableRateLimiting; private bool _enableRateLimiting;
public RateLimitOptions _rateLimitOptions; private RateLimitOptions _rateLimitOptions;
private string _authenticationProviderKey;
private bool _useServiceDiscovery; private bool _useServiceDiscovery;
private string _serviceName; private string _serviceName;
private List<HeaderFindAndReplace> _upstreamHeaderFindAndReplace; private List<HeaderFindAndReplace> _upstreamHeaderFindAndReplace;
private List<HeaderFindAndReplace> _downstreamHeaderFindAndReplace; private List<HeaderFindAndReplace> _downstreamHeaderFindAndReplace;
private readonly List<DownstreamHostAndPort> _downstreamAddresses;
public ReRouteBuilder()
{
_downstreamAddresses = new List<DownstreamHostAndPort>();
}
public ReRouteBuilder WithDownstreamAddresses(List<DownstreamHostAndPort> downstreamAddresses)
{
_downstreamAddresses.AddRange(downstreamAddresses);
return this;
}
public ReRouteBuilder WithLoadBalancer(string loadBalancer) public ReRouteBuilder WithLoadBalancer(string loadBalancer)
{ {
@ -51,12 +60,6 @@ namespace Ocelot.Configuration.Builder
return this; return this;
} }
public ReRouteBuilder WithDownstreamHost(string downstreamHost)
{
_downstreamHost = downstreamHost;
return this;
}
public ReRouteBuilder WithDownstreamPathTemplate(string input) public ReRouteBuilder WithDownstreamPathTemplate(string input)
{ {
_downstreamPathTemplate = input; _downstreamPathTemplate = input;
@ -135,12 +138,6 @@ namespace Ocelot.Configuration.Builder
return this; return this;
} }
public ReRouteBuilder WithDownstreamPort(int port)
{
_downstreamPort = port;
return this;
}
public ReRouteBuilder WithIsQos(bool input) public ReRouteBuilder WithIsQos(bool input)
{ {
_useQos = input; _useQos = input;
@ -177,12 +174,6 @@ namespace Ocelot.Configuration.Builder
return this; return this;
} }
public ReRouteBuilder WithAuthenticationProviderKey(string authenticationProviderKey)
{
_authenticationProviderKey = authenticationProviderKey;
return this;
}
public ReRouteBuilder WithHttpHandlerOptions(HttpHandlerOptions input) public ReRouteBuilder WithHttpHandlerOptions(HttpHandlerOptions input)
{ {
_httpHandlerOptions = input; _httpHandlerOptions = input;
@ -232,8 +223,6 @@ namespace Ocelot.Configuration.Builder
_fileCacheOptions, _fileCacheOptions,
_downstreamScheme, _downstreamScheme,
_loadBalancer, _loadBalancer,
_downstreamHost,
_downstreamPort,
_loadBalancerKey, _loadBalancerKey,
_useQos, _useQos,
_qosOptions, _qosOptions,
@ -243,7 +232,8 @@ namespace Ocelot.Configuration.Builder
_useServiceDiscovery, _useServiceDiscovery,
_serviceName, _serviceName,
_upstreamHeaderFindAndReplace, _upstreamHeaderFindAndReplace,
_downstreamHeaderFindAndReplace); _downstreamHeaderFindAndReplace,
_downstreamAddresses);
} }
} }
} }

View File

@ -0,0 +1,14 @@
using System.Collections.Generic;
using System.Linq;
using Ocelot.Configuration.File;
namespace Ocelot.Configuration.Creator
{
public class DownstreamAddressesCreator : IDownstreamAddressesCreator
{
public List<DownstreamHostAndPort> Create(FileReRoute reRoute)
{
return reRoute.DownstreamHostAndPorts.Select(hostAndPort => new DownstreamHostAndPort(hostAndPort.Host, hostAndPort.Port)).ToList();
}
}
}

View File

@ -38,6 +38,7 @@ namespace Ocelot.Configuration.Creator
private readonly IHttpHandlerOptionsCreator _httpHandlerOptionsCreator; private readonly IHttpHandlerOptionsCreator _httpHandlerOptionsCreator;
private readonly IAdministrationPath _adminPath; private readonly IAdministrationPath _adminPath;
private readonly IHeaderFindAndReplaceCreator _headerFAndRCreator; private readonly IHeaderFindAndReplaceCreator _headerFAndRCreator;
private readonly IDownstreamAddressesCreator _downstreamAddressesCreator;
public FileOcelotConfigurationCreator( public FileOcelotConfigurationCreator(
@ -55,9 +56,11 @@ namespace Ocelot.Configuration.Creator
IRegionCreator regionCreator, IRegionCreator regionCreator,
IHttpHandlerOptionsCreator httpHandlerOptionsCreator, IHttpHandlerOptionsCreator httpHandlerOptionsCreator,
IAdministrationPath adminPath, IAdministrationPath adminPath,
IHeaderFindAndReplaceCreator headerFAndRCreator IHeaderFindAndReplaceCreator headerFAndRCreator,
IDownstreamAddressesCreator downstreamAddressesCreator
) )
{ {
_downstreamAddressesCreator = downstreamAddressesCreator;
_headerFAndRCreator = headerFAndRCreator; _headerFAndRCreator = headerFAndRCreator;
_adminPath = adminPath; _adminPath = adminPath;
_regionCreator = regionCreator; _regionCreator = regionCreator;
@ -133,6 +136,8 @@ namespace Ocelot.Configuration.Creator
var hAndRs = _headerFAndRCreator.Create(fileReRoute); var hAndRs = _headerFAndRCreator.Create(fileReRoute);
var downstreamAddresses = _downstreamAddressesCreator.Create(fileReRoute);
var reRoute = new ReRouteBuilder() var reRoute = new ReRouteBuilder()
.WithDownstreamPathTemplate(fileReRoute.DownstreamPathTemplate) .WithDownstreamPathTemplate(fileReRoute.DownstreamPathTemplate)
.WithUpstreamPathTemplate(fileReRoute.UpstreamPathTemplate) .WithUpstreamPathTemplate(fileReRoute.UpstreamPathTemplate)
@ -150,8 +155,7 @@ namespace Ocelot.Configuration.Creator
.WithCacheOptions(new CacheOptions(fileReRoute.FileCacheOptions.TtlSeconds, region)) .WithCacheOptions(new CacheOptions(fileReRoute.FileCacheOptions.TtlSeconds, region))
.WithDownstreamScheme(fileReRoute.DownstreamScheme) .WithDownstreamScheme(fileReRoute.DownstreamScheme)
.WithLoadBalancer(fileReRoute.LoadBalancer) .WithLoadBalancer(fileReRoute.LoadBalancer)
.WithDownstreamHost(fileReRoute.DownstreamHost) .WithDownstreamAddresses(downstreamAddresses)
.WithDownstreamPort(fileReRoute.DownstreamPort)
.WithReRouteKey(reRouteKey) .WithReRouteKey(reRouteKey)
.WithIsQos(fileReRouteOptions.IsQos) .WithIsQos(fileReRouteOptions.IsQos)
.WithQosOptions(qosOptions) .WithQosOptions(qosOptions)

View File

@ -0,0 +1,10 @@
using System.Collections.Generic;
using Ocelot.Configuration.File;
namespace Ocelot.Configuration.Creator
{
public interface IDownstreamAddressesCreator
{
List<DownstreamHostAndPort> Create(FileReRoute reRoute);
}
}

View File

@ -0,0 +1,13 @@
namespace Ocelot.Configuration
{
public class DownstreamHostAndPort
{
public DownstreamHostAndPort(string host, int port)
{
Host = host;
Port = port;
}
public string Host { get; private set; }
public int Port { get; private set; }
}
}

View File

@ -0,0 +1,8 @@
namespace Ocelot.Configuration.File
{
public class FileHostAndPort
{
public string Host {get;set;}
public int Port { get; set; }
}
}

View File

@ -18,6 +18,7 @@ namespace Ocelot.Configuration.File
AuthenticationOptions = new FileAuthenticationOptions(); AuthenticationOptions = new FileAuthenticationOptions();
HttpHandlerOptions = new FileHttpHandlerOptions(); HttpHandlerOptions = new FileHttpHandlerOptions();
UpstreamHeaderTransform = new Dictionary<string, string>(); UpstreamHeaderTransform = new Dictionary<string, string>();
DownstreamHostAndPorts = new List<FileHostAndPort>();
} }
public string DownstreamPathTemplate { get; set; } public string DownstreamPathTemplate { get; set; }
@ -34,13 +35,12 @@ namespace Ocelot.Configuration.File
public bool ReRouteIsCaseSensitive { get; set; } public bool ReRouteIsCaseSensitive { get; set; }
public string ServiceName { get; set; } public string ServiceName { get; set; }
public string DownstreamScheme {get;set;} public string DownstreamScheme {get;set;}
public string DownstreamHost {get;set;}
public int DownstreamPort { get; set; }
public FileQoSOptions QoSOptions { get; set; } public FileQoSOptions QoSOptions { get; set; }
public string LoadBalancer {get;set;} public string LoadBalancer {get;set;}
public FileRateLimitRule RateLimitOptions { get; set; } public FileRateLimitRule RateLimitOptions { get; set; }
public FileAuthenticationOptions AuthenticationOptions { get; set; } public FileAuthenticationOptions AuthenticationOptions { get; set; }
public FileHttpHandlerOptions HttpHandlerOptions { get; set; } public FileHttpHandlerOptions HttpHandlerOptions { get; set; }
public bool UseServiceDiscovery {get;set;} public bool UseServiceDiscovery {get;set;}
public List<FileHostAndPort> DownstreamHostAndPorts {get;set;}
} }
} }

View File

@ -23,8 +23,6 @@ namespace Ocelot.Configuration
CacheOptions cacheOptions, CacheOptions cacheOptions,
string downstreamScheme, string downstreamScheme,
string loadBalancer, string loadBalancer,
string downstreamHost,
int downstreamPort,
string reRouteKey, string reRouteKey,
bool isQos, bool isQos,
QoSOptions qosOptions, QoSOptions qosOptions,
@ -34,7 +32,8 @@ namespace Ocelot.Configuration
bool useServiceDiscovery, bool useServiceDiscovery,
string serviceName, string serviceName,
List<HeaderFindAndReplace> upstreamHeadersFindAndReplace, List<HeaderFindAndReplace> upstreamHeadersFindAndReplace,
List<HeaderFindAndReplace> downstreamHeadersFindAndReplace) List<HeaderFindAndReplace> downstreamHeadersFindAndReplace,
List<DownstreamHostAndPort> downstreamAddresses)
{ {
DownstreamHeadersFindAndReplace = downstreamHeadersFindAndReplace; DownstreamHeadersFindAndReplace = downstreamHeadersFindAndReplace;
UpstreamHeadersFindAndReplace = upstreamHeadersFindAndReplace; UpstreamHeadersFindAndReplace = upstreamHeadersFindAndReplace;
@ -42,8 +41,7 @@ namespace Ocelot.Configuration
UseServiceDiscovery = useServiceDiscovery; UseServiceDiscovery = useServiceDiscovery;
ReRouteKey = reRouteKey; ReRouteKey = reRouteKey;
LoadBalancer = loadBalancer; LoadBalancer = loadBalancer;
DownstreamHost = downstreamHost; DownstreamAddresses = downstreamAddresses;
DownstreamPort = downstreamPort;
DownstreamPathTemplate = downstreamPathTemplate; DownstreamPathTemplate = downstreamPathTemplate;
UpstreamPathTemplate = upstreamPathTemplate; UpstreamPathTemplate = upstreamPathTemplate;
UpstreamHttpMethod = upstreamHttpMethod; UpstreamHttpMethod = upstreamHttpMethod;
@ -88,8 +86,6 @@ namespace Ocelot.Configuration
public bool IsQos { get; private set; } public bool IsQos { get; private set; }
public QoSOptions QosOptionsOptions { get; private set; } public QoSOptions QosOptionsOptions { get; private set; }
public string LoadBalancer {get;private set;} public string LoadBalancer {get;private set;}
public string DownstreamHost { get; private set; }
public int DownstreamPort { get; private set; }
public bool EnableEndpointEndpointRateLimiting { get; private set; } public bool EnableEndpointEndpointRateLimiting { get; private set; }
public RateLimitOptions RateLimitOptions { get; private set; } public RateLimitOptions RateLimitOptions { get; private set; }
public HttpHandlerOptions HttpHandlerOptions { get; private set; } public HttpHandlerOptions HttpHandlerOptions { get; private set; }
@ -97,6 +93,7 @@ namespace Ocelot.Configuration
public string ServiceName {get;private set;} public string ServiceName {get;private set;}
public List<HeaderFindAndReplace> UpstreamHeadersFindAndReplace {get;private set;} public List<HeaderFindAndReplace> UpstreamHeadersFindAndReplace {get;private set;}
public List<HeaderFindAndReplace> DownstreamHeadersFindAndReplace {get;private set;} public List<HeaderFindAndReplace> DownstreamHeadersFindAndReplace {get;private set;}
public List<DownstreamHostAndPort> DownstreamAddresses {get;private set;}
} }
} }

View File

@ -0,0 +1,13 @@
using FluentValidation;
using Ocelot.Configuration.File;
namespace Ocelot.Configuration.Validator
{
public class HostAndPortValidator : AbstractValidator<FileHostAndPort>
{
public HostAndPortValidator()
{
RuleFor(r => r.Host).NotEmpty().WithMessage("When not using service discovery Host must be set on DownstreamHostAndPorts if you are not using ReRoute.Host or Ocelot cannot find your service!");
}
}
}

View File

@ -52,7 +52,12 @@ namespace Ocelot.Configuration.Validator
}); });
When(reRoute => !reRoute.UseServiceDiscovery, () => { When(reRoute => !reRoute.UseServiceDiscovery, () => {
RuleFor(r => r.DownstreamHost).NotEmpty().WithMessage("When not using service discover DownstreamHost must be set or Ocelot cannot find your service!"); RuleFor(r => r.DownstreamHostAndPorts).NotEmpty().WithMessage("When not using service discovery DownstreamHostAndPorts must be set and not empty or Ocelot cannot find your service!");
});
When(reRoute => !reRoute.UseServiceDiscovery, () => {
RuleFor(reRoute => reRoute.DownstreamHostAndPorts)
.SetCollectionValidator(new HostAndPortValidator());
}); });
} }

View File

@ -0,0 +1,12 @@
namespace Ocelot.DependencyInjection
{
public class AdministrationPath : IAdministrationPath
{
public AdministrationPath(string path)
{
Path = path;
}
public string Path {get;private set;}
}
}

View File

@ -0,0 +1,7 @@
namespace Ocelot.DependencyInjection
{
public interface IAdministrationPath
{
string Path {get;}
}
}

View File

@ -0,0 +1,12 @@
namespace Ocelot.DependencyInjection
{
public class NullAdministrationPath : IAdministrationPath
{
public NullAdministrationPath()
{
Path = null;
}
public string Path {get;private set;}
}
}

View File

@ -122,6 +122,7 @@ namespace Ocelot.DependencyInjection
_services.TryAddSingleton<IHttpClientCache, MemoryHttpClientCache>(); _services.TryAddSingleton<IHttpClientCache, MemoryHttpClientCache>();
_services.TryAddSingleton<IRequestMapper, RequestMapper>(); _services.TryAddSingleton<IRequestMapper, RequestMapper>();
_services.TryAddSingleton<IHttpHandlerOptionsCreator, HttpHandlerOptionsCreator>(); _services.TryAddSingleton<IHttpHandlerOptionsCreator, HttpHandlerOptionsCreator>();
_services.TryAddSingleton<IDownstreamAddressesCreator, DownstreamAddressesCreator>();
// see this for why we register this as singleton http://stackoverflow.com/questions/37371264/invalidoperationexception-unable-to-resolve-service-for-type-microsoft-aspnetc // see this for why we register this as singleton http://stackoverflow.com/questions/37371264/invalidoperationexception-unable-to-resolve-service-for-type-microsoft-aspnetc
// could maybe use a scoped data repository // could maybe use a scoped data repository
_services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>(); _services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
@ -303,29 +304,4 @@ namespace Ocelot.DependencyInjection
return this; return this;
} }
} }
public interface IAdministrationPath
{
string Path {get;}
}
public class NullAdministrationPath : IAdministrationPath
{
public NullAdministrationPath()
{
Path = null;
}
public string Path {get;private set;}
}
public class AdministrationPath : IAdministrationPath
{
public AdministrationPath(string path)
{
Path = path;
}
public string Path {get;private set;}
}
} }

View File

@ -5,6 +5,6 @@ namespace Ocelot.DownstreamUrlCreator
{ {
public interface IUrlBuilder public interface IUrlBuilder
{ {
Response<DownstreamUrl> Build(string downstreamPath, string downstreamScheme, HostAndPort downstreamHostAndPort); Response<DownstreamUrl> Build(string downstreamPath, string downstreamScheme, ServiceHostAndPort downstreamHostAndPort);
} }
} }

View File

@ -8,7 +8,7 @@ namespace Ocelot.DownstreamUrlCreator
{ {
public class UrlBuilder : IUrlBuilder public class UrlBuilder : IUrlBuilder
{ {
public Response<DownstreamUrl> Build(string downstreamPath, string downstreamScheme, HostAndPort downstreamHostAndPort) public Response<DownstreamUrl> Build(string downstreamPath, string downstreamScheme, ServiceHostAndPort downstreamHostAndPort)
{ {
if (string.IsNullOrEmpty(downstreamPath)) if (string.IsNullOrEmpty(downstreamPath))
{ {

View File

@ -1,24 +1,55 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using Ocelot.Configuration; using Ocelot.Configuration;
using Ocelot.Infrastructure.Extensions;
using Ocelot.Responses; using Ocelot.Responses;
namespace Ocelot.Headers namespace Ocelot.Headers
{ {
public class HttpResponseHeaderReplacer : IHttpResponseHeaderReplacer public class HttpResponseHeaderReplacer : IHttpResponseHeaderReplacer
{ {
public Response Replace(HttpResponseMessage response, List<HeaderFindAndReplace> fAndRs) private Dictionary<string, Func<HttpRequestMessage, string>> _placeholders;
public HttpResponseHeaderReplacer()
{
_placeholders = new Dictionary<string, Func<HttpRequestMessage, string>>();
_placeholders.Add("{DownstreamBaseUrl}", x => {
var downstreamUrl = $"{x.RequestUri.Scheme}://{x.RequestUri.Host}";
if(x.RequestUri.Port != 80 && x.RequestUri.Port != 443)
{
downstreamUrl = $"{downstreamUrl}:{x.RequestUri.Port}";
}
return $"{downstreamUrl}/";
});
}
public Response Replace(HttpResponseMessage response, List<HeaderFindAndReplace> fAndRs, HttpRequestMessage httpRequestMessage)
{ {
foreach (var f in fAndRs) foreach (var f in fAndRs)
{ {
//if the response headers contain a matching find and replace
if(response.Headers.TryGetValues(f.Key, out var values)) if(response.Headers.TryGetValues(f.Key, out var values))
{
//check to see if it is a placeholder in the find...
if(_placeholders.TryGetValue(f.Find, out var replacePlaceholder))
{
//if it is we need to get the value of the placeholder
var find = replacePlaceholder(httpRequestMessage);
var replaced = values.ToList()[f.Index].Replace(find, f.Replace.LastCharAsForwardSlash());
response.Headers.Remove(f.Key);
response.Headers.Add(f.Key, replaced);
}
else
{ {
var replaced = values.ToList()[f.Index].Replace(f.Find, f.Replace); var replaced = values.ToList()[f.Index].Replace(f.Find, f.Replace);
response.Headers.Remove(f.Key); response.Headers.Remove(f.Key);
response.Headers.Add(f.Key, replaced); response.Headers.Add(f.Key, replaced);
} }
} }
}
return new OkResponse(); return new OkResponse();
} }

View File

@ -7,6 +7,6 @@ namespace Ocelot.Headers
{ {
public interface IHttpResponseHeaderReplacer public interface IHttpResponseHeaderReplacer
{ {
Response Replace(HttpResponseMessage response, List<HeaderFindAndReplace> fAndRs); Response Replace(HttpResponseMessage response, List<HeaderFindAndReplace> fAndRs, HttpRequestMessage httpRequestMessage);
} }
} }

View File

@ -36,7 +36,7 @@ namespace Ocelot.Headers.Middleware
var postFAndRs = this.DownstreamRoute.ReRoute.DownstreamHeadersFindAndReplace; var postFAndRs = this.DownstreamRoute.ReRoute.DownstreamHeadersFindAndReplace;
_postReplacer.Replace(HttpResponseMessage, postFAndRs); _postReplacer.Replace(HttpResponseMessage, postFAndRs, DownstreamRequest);
} }
} }
} }

View File

@ -20,5 +20,14 @@ namespace Ocelot.Infrastructure.Extensions
return s; return s;
} }
public static string LastCharAsForwardSlash(this string source)
{
if(source.EndsWith('/'))
{
return source;
}
return $"{source}/";
}
} }
} }

View File

@ -6,7 +6,7 @@ namespace Ocelot.LoadBalancer.LoadBalancers
{ {
public interface ILoadBalancer public interface ILoadBalancer
{ {
Task<Response<HostAndPort>> Lease(); Task<Response<ServiceHostAndPort>> Lease();
void Release(HostAndPort hostAndPort); void Release(ServiceHostAndPort hostAndPort);
} }
} }

View File

@ -4,12 +4,12 @@ namespace Ocelot.LoadBalancer.LoadBalancers
{ {
public class Lease public class Lease
{ {
public Lease(HostAndPort hostAndPort, int connections) public Lease(ServiceHostAndPort hostAndPort, int connections)
{ {
HostAndPort = hostAndPort; HostAndPort = hostAndPort;
Connections = connections; Connections = connections;
} }
public HostAndPort HostAndPort { get; private set; } public ServiceHostAndPort HostAndPort { get; private set; }
public int Connections { get; private set; } public int Connections { get; private set; }
} }
} }

View File

@ -22,18 +22,18 @@ namespace Ocelot.LoadBalancer.LoadBalancers
_leases = new List<Lease>(); _leases = new List<Lease>();
} }
public async Task<Response<HostAndPort>> Lease() public async Task<Response<ServiceHostAndPort>> Lease()
{ {
var services = await _services.Invoke(); var services = await _services.Invoke();
if (services == null) if (services == null)
{ {
return new ErrorResponse<HostAndPort>(new List<Error>() { new ServicesAreNullError($"services were null for {_serviceName}") }); return new ErrorResponse<ServiceHostAndPort>(new List<Error>() { new ServicesAreNullError($"services were null for {_serviceName}") });
} }
if (!services.Any()) if (!services.Any())
{ {
return new ErrorResponse<HostAndPort>(new List<Error>() { new ServicesAreEmptyError($"services were empty for {_serviceName}") }); return new ErrorResponse<ServiceHostAndPort>(new List<Error>() { new ServicesAreEmptyError($"services were empty for {_serviceName}") });
} }
lock(_syncLock) lock(_syncLock)
@ -49,11 +49,11 @@ namespace Ocelot.LoadBalancer.LoadBalancers
_leases.Add(leaseWithLeastConnections); _leases.Add(leaseWithLeastConnections);
return new OkResponse<HostAndPort>(new HostAndPort(leaseWithLeastConnections.HostAndPort.DownstreamHost, leaseWithLeastConnections.HostAndPort.DownstreamPort)); return new OkResponse<ServiceHostAndPort>(new ServiceHostAndPort(leaseWithLeastConnections.HostAndPort.DownstreamHost, leaseWithLeastConnections.HostAndPort.DownstreamPort));
} }
} }
public void Release(HostAndPort hostAndPort) public void Release(ServiceHostAndPort hostAndPort)
{ {
lock(_syncLock) lock(_syncLock)
{ {

View File

@ -15,13 +15,13 @@ namespace Ocelot.LoadBalancer.LoadBalancers
_services = services; _services = services;
} }
public async Task<Response<HostAndPort>> Lease() public async Task<Response<ServiceHostAndPort>> Lease()
{ {
var service = await Task.FromResult(_services.FirstOrDefault()); var service = await Task.FromResult(_services.FirstOrDefault());
return new OkResponse<HostAndPort>(service.HostAndPort); return new OkResponse<ServiceHostAndPort>(service.HostAndPort);
} }
public void Release(HostAndPort hostAndPort) public void Release(ServiceHostAndPort hostAndPort)
{ {
} }
} }

View File

@ -18,7 +18,7 @@ namespace Ocelot.LoadBalancer.LoadBalancers
} }
public async Task<Response<HostAndPort>> Lease() public async Task<Response<ServiceHostAndPort>> Lease()
{ {
var services = await _services.Invoke(); var services = await _services.Invoke();
if (_last >= services.Count) if (_last >= services.Count)
@ -28,10 +28,10 @@ namespace Ocelot.LoadBalancer.LoadBalancers
var next = await Task.FromResult(services[_last]); var next = await Task.FromResult(services[_last]);
_last++; _last++;
return new OkResponse<HostAndPort>(next.HostAndPort); return new OkResponse<ServiceHostAndPort>(next.HostAndPort);
} }
public void Release(HostAndPort hostAndPort) public void Release(ServiceHostAndPort hostAndPort)
{ {
} }
} }

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;

View File

@ -39,7 +39,7 @@ namespace Ocelot.ServiceDiscovery
{ {
return new Service( return new Service(
serviceEntry.Service.Service, serviceEntry.Service.Service,
new HostAndPort(serviceEntry.Service.Address, serviceEntry.Service.Port), new ServiceHostAndPort(serviceEntry.Service.Address, serviceEntry.Service.Port),
serviceEntry.Service.ID, serviceEntry.Service.ID,
GetVersionFromStrings(serviceEntry.Service.Tags), GetVersionFromStrings(serviceEntry.Service.Tags),
serviceEntry.Service.Tags ?? Enumerable.Empty<string>()); serviceEntry.Service.Tags ?? Enumerable.Empty<string>());

View File

@ -13,14 +13,14 @@ namespace Ocelot.ServiceDiscovery
return GetServiceDiscoveryProvider(reRoute.ServiceName, serviceConfig.ServiceProviderHost, serviceConfig.ServiceProviderPort); return GetServiceDiscoveryProvider(reRoute.ServiceName, serviceConfig.ServiceProviderHost, serviceConfig.ServiceProviderPort);
} }
var services = new List<Service>() var services = new List<Service>();
foreach (var downstreamAddress in reRoute.DownstreamAddresses)
{ {
new Service(reRoute.ServiceName, var service = new Service(reRoute.ServiceName, new ServiceHostAndPort(downstreamAddress.Host, downstreamAddress.Port), string.Empty, string.Empty, new string[0]);
new HostAndPort(reRoute.DownstreamHost, reRoute.DownstreamPort),
string.Empty, services.Add(service);
string.Empty, }
new string[0])
};
return new ConfigurationServiceProvider(services); return new ConfigurationServiceProvider(services);
} }

View File

@ -5,7 +5,7 @@ namespace Ocelot.Values
public class Service public class Service
{ {
public Service(string name, public Service(string name,
HostAndPort hostAndPort, ServiceHostAndPort hostAndPort,
string id, string id,
string version, string version,
IEnumerable<string> tags) IEnumerable<string> tags)
@ -24,6 +24,6 @@ namespace Ocelot.Values
public IEnumerable<string> Tags { get; private set; } public IEnumerable<string> Tags { get; private set; }
public HostAndPort HostAndPort { get; private set; } public ServiceHostAndPort HostAndPort { get; private set; }
} }
} }

View File

@ -1,8 +1,8 @@
namespace Ocelot.Values namespace Ocelot.Values
{ {
public class HostAndPort public class ServiceHostAndPort
{ {
public HostAndPort(string downstreamHost, int downstreamPort) public ServiceHostAndPort(string downstreamHost, int downstreamPort)
{ {
DownstreamHost = downstreamHost?.Trim('/'); DownstreamHost = downstreamHost?.Trim('/');
DownstreamPort = downstreamPort; DownstreamPort = downstreamPort;

View File

@ -53,8 +53,14 @@ namespace Ocelot.AcceptanceTests
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = _downstreamServicePath, DownstreamPathTemplate = _downstreamServicePath,
DownstreamPort = _downstreamServicePort, DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamHost = _downstreamServiceHost, {
new FileHostAndPort
{
Host =_downstreamServiceHost,
Port = _downstreamServicePort,
}
},
DownstreamScheme = _downstreamServiceScheme, DownstreamScheme = _downstreamServiceScheme,
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Post" }, UpstreamHttpMethod = new List<string> { "Post" },
@ -86,8 +92,14 @@ namespace Ocelot.AcceptanceTests
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = _downstreamServicePath, DownstreamPathTemplate = _downstreamServicePath,
DownstreamPort = _downstreamServicePort, DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamHost = _downstreamServiceHost, {
new FileHostAndPort
{
Host =_downstreamServiceHost,
Port = _downstreamServicePort,
}
},
DownstreamScheme = _downstreamServiceScheme, DownstreamScheme = _downstreamServiceScheme,
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
@ -121,8 +133,14 @@ namespace Ocelot.AcceptanceTests
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = _downstreamServicePath, DownstreamPathTemplate = _downstreamServicePath,
DownstreamPort = _downstreamServicePort, DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamHost = _downstreamServiceHost, {
new FileHostAndPort
{
Host =_downstreamServiceHost,
Port = _downstreamServicePort,
}
},
DownstreamScheme = _downstreamServiceScheme, DownstreamScheme = _downstreamServiceScheme,
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
@ -155,8 +173,14 @@ namespace Ocelot.AcceptanceTests
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = _downstreamServicePath, DownstreamPathTemplate = _downstreamServicePath,
DownstreamPort = _downstreamServicePort, DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamHost = _downstreamServiceHost, {
new FileHostAndPort
{
Host =_downstreamServiceHost,
Port = _downstreamServicePort,
}
},
DownstreamScheme = _downstreamServiceScheme, DownstreamScheme = _downstreamServiceScheme,
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Post" }, UpstreamHttpMethod = new List<string> { "Post" },
@ -190,8 +214,14 @@ namespace Ocelot.AcceptanceTests
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = _downstreamServicePath, DownstreamPathTemplate = _downstreamServicePath,
DownstreamPort = _downstreamServicePort, DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamHost = _downstreamServiceHost, {
new FileHostAndPort
{
Host =_downstreamServiceHost,
Port = _downstreamServicePort,
}
},
DownstreamScheme = _downstreamServiceScheme, DownstreamScheme = _downstreamServiceScheme,
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Post" }, UpstreamHttpMethod = new List<string> { "Post" },

View File

@ -49,9 +49,15 @@ namespace Ocelot.AcceptanceTests
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamPort = 51876, DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51876,
}
},
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost",
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
AuthenticationOptions = new FileAuthenticationOptions AuthenticationOptions = new FileAuthenticationOptions
@ -101,9 +107,15 @@ namespace Ocelot.AcceptanceTests
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamPort = 51876, DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51876,
}
},
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost",
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
AuthenticationOptions = new FileAuthenticationOptions AuthenticationOptions = new FileAuthenticationOptions
@ -151,8 +163,14 @@ namespace Ocelot.AcceptanceTests
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamPort = 51876, DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamHost = "localhost", {
new FileHostAndPort
{
Host = "localhost",
Port = 51876,
}
},
DownstreamScheme = "http", DownstreamScheme = "http",
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
@ -186,8 +204,14 @@ namespace Ocelot.AcceptanceTests
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamPort = 51876, DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamHost = "localhost", {
new FileHostAndPort
{
Host = "localhost",
Port = 51876,
}
},
DownstreamScheme = "http", DownstreamScheme = "http",
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },

View File

@ -32,9 +32,15 @@ namespace Ocelot.AcceptanceTests
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamPort = 51879, DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
},
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost",
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
FileCacheOptions = new FileCacheOptions FileCacheOptions = new FileCacheOptions
@ -68,9 +74,15 @@ namespace Ocelot.AcceptanceTests
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamPort = 51879, DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
},
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost",
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
FileCacheOptions = new FileCacheOptions FileCacheOptions = new FileCacheOptions
@ -104,9 +116,15 @@ namespace Ocelot.AcceptanceTests
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamPort = 51879, DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
},
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost",
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
FileCacheOptions = new FileCacheOptions FileCacheOptions = new FileCacheOptions

View File

@ -31,9 +31,15 @@ namespace Ocelot.AcceptanceTests
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/api/products/{productId}", DownstreamPathTemplate = "/api/products/{productId}",
DownstreamPort = 51879, DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
},
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost",
UpstreamPathTemplate = "/products/{productId}", UpstreamPathTemplate = "/products/{productId}",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
} }
@ -58,9 +64,15 @@ namespace Ocelot.AcceptanceTests
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/api/products/{productId}", DownstreamPathTemplate = "/api/products/{productId}",
DownstreamPort = 51879, DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
},
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost",
UpstreamPathTemplate = "/products/{productId}", UpstreamPathTemplate = "/products/{productId}",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
ReRouteIsCaseSensitive = false, ReRouteIsCaseSensitive = false,
@ -86,9 +98,15 @@ namespace Ocelot.AcceptanceTests
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/api/products/{productId}", DownstreamPathTemplate = "/api/products/{productId}",
DownstreamPort = 51879, DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
},
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost",
UpstreamPathTemplate = "/products/{productId}", UpstreamPathTemplate = "/products/{productId}",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
ReRouteIsCaseSensitive = true, ReRouteIsCaseSensitive = true,
@ -114,9 +132,15 @@ namespace Ocelot.AcceptanceTests
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/api/products/{productId}", DownstreamPathTemplate = "/api/products/{productId}",
DownstreamPort = 51879, DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
},
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost",
UpstreamPathTemplate = "/PRODUCTS/{productId}", UpstreamPathTemplate = "/PRODUCTS/{productId}",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
ReRouteIsCaseSensitive = true, ReRouteIsCaseSensitive = true,
@ -142,9 +166,15 @@ namespace Ocelot.AcceptanceTests
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/api/products/{productId}", DownstreamPathTemplate = "/api/products/{productId}",
DownstreamPort = 51879, DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
},
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost",
UpstreamPathTemplate = "/products/{productId}", UpstreamPathTemplate = "/products/{productId}",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
ReRouteIsCaseSensitive = true, ReRouteIsCaseSensitive = true,
@ -170,9 +200,15 @@ namespace Ocelot.AcceptanceTests
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/api/products/{productId}", DownstreamPathTemplate = "/api/products/{productId}",
DownstreamPort = 51879, DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
},
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost",
UpstreamPathTemplate = "/PRODUCTS/{productId}", UpstreamPathTemplate = "/PRODUCTS/{productId}",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
ReRouteIsCaseSensitive = true, ReRouteIsCaseSensitive = true,

View File

@ -63,9 +63,15 @@ namespace Ocelot.AcceptanceTests
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamPort = 52876, DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 52876,
}
},
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost",
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
AuthenticationOptions = new FileAuthenticationOptions AuthenticationOptions = new FileAuthenticationOptions

View File

@ -63,9 +63,15 @@ namespace Ocelot.AcceptanceTests
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamPort = 57876, DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 57876,
}
},
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost",
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
AuthenticationOptions = new FileAuthenticationOptions AuthenticationOptions = new FileAuthenticationOptions

View File

@ -43,9 +43,15 @@ namespace Ocelot.AcceptanceTests
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/api/ClientRateLimit", DownstreamPathTemplate = "/api/ClientRateLimit",
DownstreamPort = 51879, DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
},
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost",
UpstreamPathTemplate = "/api/ClientRateLimit", UpstreamPathTemplate = "/api/ClientRateLimit",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
RequestIdKey = _steps.RequestIdKey, RequestIdKey = _steps.RequestIdKey,
@ -98,9 +104,15 @@ namespace Ocelot.AcceptanceTests
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/api/ClientRateLimit", DownstreamPathTemplate = "/api/ClientRateLimit",
DownstreamPort = 51879, DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
},
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost",
UpstreamPathTemplate = "/api/ClientRateLimit", UpstreamPathTemplate = "/api/ClientRateLimit",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
RequestIdKey = _steps.RequestIdKey, RequestIdKey = _steps.RequestIdKey,

View File

@ -40,8 +40,14 @@ namespace Ocelot.AcceptanceTests
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 51779, {
new FileHostAndPort
{
Host = "localhost",
Port = 51779,
}
},
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
} }
@ -79,8 +85,14 @@ namespace Ocelot.AcceptanceTests
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 51779, {
new FileHostAndPort
{
Host = "localhost",
Port = 51779,
}
},
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
} }
@ -133,8 +145,14 @@ namespace Ocelot.AcceptanceTests
{ {
DownstreamPathTemplate = "/status", DownstreamPathTemplate = "/status",
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 51779, {
new FileHostAndPort
{
Host = "localhost",
Port = 51779,
}
},
UpstreamPathTemplate = "/cs/status", UpstreamPathTemplate = "/cs/status",
UpstreamHttpMethod = new List<string> {"Get"} UpstreamHttpMethod = new List<string> {"Get"}
} }
@ -187,8 +205,14 @@ namespace Ocelot.AcceptanceTests
{ {
DownstreamPathTemplate = "/status", DownstreamPathTemplate = "/status",
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 51779, {
new FileHostAndPort
{
Host = "localhost",
Port = 51780,
}
},
UpstreamPathTemplate = "/cs/status", UpstreamPathTemplate = "/cs/status",
UpstreamHttpMethod = new List<string> {"Get"} UpstreamHttpMethod = new List<string> {"Get"}
} }
@ -211,8 +235,14 @@ namespace Ocelot.AcceptanceTests
{ {
DownstreamPathTemplate = "/status", DownstreamPathTemplate = "/status",
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 51779, {
new FileHostAndPort
{
Host = "localhost",
Port = 51780,
}
},
UpstreamPathTemplate = "/cs/status/awesome", UpstreamPathTemplate = "/cs/status/awesome",
UpstreamHttpMethod = new List<string> {"Get"} UpstreamHttpMethod = new List<string> {"Get"}
} }
@ -229,7 +259,7 @@ namespace Ocelot.AcceptanceTests
this.Given(x => GivenTheConsulConfigurationIs(consulConfig)) this.Given(x => GivenTheConsulConfigurationIs(consulConfig))
.And(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl)) .And(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl))
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51779", "/status", 200, "Hello from Laura")) .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51780", "/status", 200, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunningUsingConsulToStoreConfig()) .And(x => _steps.GivenOcelotIsRunningUsingConsulToStoreConfig())
.And(x => _steps.WhenIGetUrlOnTheApiGateway("/cs/status")) .And(x => _steps.WhenIGetUrlOnTheApiGateway("/cs/status"))

View File

@ -46,9 +46,15 @@ namespace Ocelot.AcceptanceTests
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamPort = 41879, DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 41879,
}
},
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost",
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
} }
@ -83,9 +89,15 @@ namespace Ocelot.AcceptanceTests
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamPort = 41879, DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 41879,
}
},
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost",
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
} }
@ -120,9 +132,15 @@ namespace Ocelot.AcceptanceTests
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/41879/", DownstreamPathTemplate = "/41879/",
DownstreamPort = 41879, DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 41879,
}
},
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost",
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
} }
@ -157,9 +175,15 @@ namespace Ocelot.AcceptanceTests
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamPort = 41879, DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 41879,
}
},
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost",
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
} }
@ -194,9 +218,15 @@ namespace Ocelot.AcceptanceTests
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamPort = 41879, DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 41879,
}
},
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost",
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
} }
@ -231,9 +261,15 @@ namespace Ocelot.AcceptanceTests
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamPort = 41879, DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 41879,
}
},
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost",
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
} }

View File

@ -36,8 +36,14 @@ namespace Ocelot.AcceptanceTests
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 51879, {
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
},
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
UpstreamHeaderTransform = new Dictionary<string,string> UpstreamHeaderTransform = new Dictionary<string,string>
@ -69,8 +75,14 @@ namespace Ocelot.AcceptanceTests
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 51879, {
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
},
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
DownstreamHeaderTransform = new Dictionary<string,string> DownstreamHeaderTransform = new Dictionary<string,string>
@ -101,8 +113,14 @@ namespace Ocelot.AcceptanceTests
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 6773, {
new FileHostAndPort
{
Host = "localhost",
Port = 6773,
}
},
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
DownstreamHeaderTransform = new Dictionary<string,string> DownstreamHeaderTransform = new Dictionary<string,string>
@ -126,6 +144,48 @@ namespace Ocelot.AcceptanceTests
.BDDfy(); .BDDfy();
} }
[Fact]
public void should_fix_issue_205()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 6773,
}
},
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
DownstreamHeaderTransform = new Dictionary<string,string>
{
{"Location", "{DownstreamBaseUrl}, {BaseUrl}"}
},
HttpHandlerOptions = new FileHttpHandlerOptions
{
AllowAutoRedirect = false
}
}
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:6773", "/", 302, "Location", "http://localhost:6773/pay/Receive"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Redirect))
.And(x => _steps.ThenTheResponseHeaderIs("Location", "http://localhost:5000/pay/Receive"))
.BDDfy();
}
private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string headerKey) private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string headerKey)
{ {

View File

@ -0,0 +1,178 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using Consul;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Ocelot.Configuration.File;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.AcceptanceTests
{
public class LoadBalancerTests : IDisposable
{
private IWebHost _builderOne;
private IWebHost _builderTwo;
private readonly Steps _steps;
private int _counterOne;
private int _counterTwo;
private static readonly object _syncLock = new object();
public LoadBalancerTests()
{
_steps = new Steps();
}
[Fact]
public void should_use_service_discovery_and_load_balance_request()
{
var downstreamServiceOneUrl = "http://localhost:50881";
var downstreamServiceTwoUrl = "http://localhost:50882";
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
LoadBalancer = "LeastConnection",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 50881
},
new FileHostAndPort
{
Host = "localhost",
Port = 50882
}
}
}
},
GlobalConfiguration = new FileGlobalConfiguration()
{
}
};
this.Given(x => x.GivenProductServiceOneIsRunning(downstreamServiceOneUrl, 200))
.And(x => x.GivenProductServiceTwoIsRunning(downstreamServiceTwoUrl, 200))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimes("/", 50))
.Then(x => x.ThenTheTwoServicesShouldHaveBeenCalledTimes(50))
.And(x => x.ThenBothServicesCalledRealisticAmountOfTimes(24, 26))
.BDDfy();
}
private void ThenOnlyOneServiceHasBeenCalled()
{
_counterOne.ShouldBe(10);
_counterTwo.ShouldBe(0);
}
private void GivenIResetCounters()
{
_counterOne = 0;
_counterTwo = 0;
}
private void ThenBothServicesCalledRealisticAmountOfTimes(int bottom, int top)
{
_counterOne.ShouldBeInRange(bottom, top);
_counterOne.ShouldBeInRange(bottom, top);
}
private void ThenTheTwoServicesShouldHaveBeenCalledTimes(int expected)
{
var total = _counterOne + _counterTwo;
total.ShouldBe(expected);
}
private void GivenProductServiceOneIsRunning(string url, int statusCode)
{
_builderOne = new WebHostBuilder()
.UseUrls(url)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseUrls(url)
.Configure(app =>
{
app.Run(async context =>
{
try
{
var response = string.Empty;
lock (_syncLock)
{
_counterOne++;
response = _counterOne.ToString();
}
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(response);
}
catch (System.Exception exception)
{
await context.Response.WriteAsync(exception.StackTrace);
}
});
})
.Build();
_builderOne.Start();
}
private void GivenProductServiceTwoIsRunning(string url, int statusCode)
{
_builderTwo = new WebHostBuilder()
.UseUrls(url)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseUrls(url)
.Configure(app =>
{
app.Run(async context =>
{
try
{
var response = string.Empty;
lock (_syncLock)
{
_counterTwo++;
response = _counterTwo.ToString();
}
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(response);
}
catch (System.Exception exception)
{
await context.Response.WriteAsync(exception.StackTrace);
}
});
})
.Build();
_builderTwo.Start();
}
public void Dispose()
{
_builderOne?.Dispose();
_builderTwo?.Dispose();
_steps.Dispose();
}
}
}

View File

@ -36,8 +36,14 @@ namespace Ocelot.AcceptanceTests
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 51879, {
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
},
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
QoSOptions = new FileQoSOptions QoSOptions = new FileQoSOptions
@ -81,8 +87,14 @@ namespace Ocelot.AcceptanceTests
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 51879, {
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
},
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
QoSOptions = new FileQoSOptions QoSOptions = new FileQoSOptions
@ -96,8 +108,14 @@ namespace Ocelot.AcceptanceTests
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 51880, {
new FileHostAndPort
{
Host = "localhost",
Port = 51880,
}
},
UpstreamPathTemplate = "/working", UpstreamPathTemplate = "/working",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
} }

View File

@ -34,9 +34,15 @@ namespace Ocelot.AcceptanceTests
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamPort = 51879, DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
},
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost",
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
RequestIdKey = _steps.RequestIdKey, RequestIdKey = _steps.RequestIdKey,
@ -62,9 +68,15 @@ namespace Ocelot.AcceptanceTests
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamPort = 51879, DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
},
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost",
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
} }
@ -91,9 +103,15 @@ namespace Ocelot.AcceptanceTests
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamPort = 51879, DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
},
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost",
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
} }

View File

@ -32,9 +32,15 @@ namespace Ocelot.AcceptanceTests
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
DownstreamPort = 53876, DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 53876,
}
},
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost"
} }
} }
}; };

View File

@ -44,10 +44,16 @@ namespace Ocelot.AcceptanceTests
{ {
DownstreamPathTemplate = "/{url}", DownstreamPathTemplate = "/{url}",
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost",
DownstreamPort = 51879,
UpstreamPathTemplate = "/{url}", UpstreamPathTemplate = "/{url}",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
}
} }
} }
}; };
@ -72,8 +78,14 @@ namespace Ocelot.AcceptanceTests
{ {
DownstreamPathTemplate = "/{url}", DownstreamPathTemplate = "/{url}",
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 51880, {
new FileHostAndPort
{
Host = "localhost",
Port = 51880,
}
},
UpstreamPathTemplate = "/{url}", UpstreamPathTemplate = "/{url}",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
}, },
@ -81,8 +93,14 @@ namespace Ocelot.AcceptanceTests
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 51879, {
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
},
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
} }
@ -109,8 +127,14 @@ namespace Ocelot.AcceptanceTests
{ {
DownstreamPathTemplate = "/{url}", DownstreamPathTemplate = "/{url}",
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 51880, {
new FileHostAndPort
{
Host = "localhost",
Port = 51880,
}
},
UpstreamPathTemplate = "/{url}", UpstreamPathTemplate = "/{url}",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
}, },
@ -118,8 +142,14 @@ namespace Ocelot.AcceptanceTests
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 51879, {
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
},
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
} }
@ -146,8 +176,14 @@ namespace Ocelot.AcceptanceTests
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 51880, {
new FileHostAndPort
{
Host = "localhost",
Port = 51880,
}
},
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
}, },
@ -155,8 +191,14 @@ namespace Ocelot.AcceptanceTests
{ {
DownstreamPathTemplate = "/{url}", DownstreamPathTemplate = "/{url}",
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 51879, {
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
},
UpstreamPathTemplate = "/{url}", UpstreamPathTemplate = "/{url}",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
} }
@ -183,8 +225,14 @@ namespace Ocelot.AcceptanceTests
{ {
DownstreamPathTemplate = "/{url}", DownstreamPathTemplate = "/{url}",
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 51879, {
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
},
UpstreamPathTemplate = "/{url}", UpstreamPathTemplate = "/{url}",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
} }
@ -211,8 +259,14 @@ namespace Ocelot.AcceptanceTests
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 51879, {
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
},
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
} }
@ -239,8 +293,14 @@ namespace Ocelot.AcceptanceTests
{ {
DownstreamPathTemplate = "/api/v1/vacancy", DownstreamPathTemplate = "/api/v1/vacancy",
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 51879, {
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
},
UpstreamPathTemplate = "/vacancy/", UpstreamPathTemplate = "/vacancy/",
UpstreamHttpMethod = new List<string> { "Options", "Put", "Get", "Post", "Delete" }, UpstreamHttpMethod = new List<string> { "Options", "Put", "Get", "Post", "Delete" },
ServiceName = "botCore", ServiceName = "botCore",
@ -250,8 +310,14 @@ namespace Ocelot.AcceptanceTests
{ {
DownstreamPathTemplate = "/api/v1/vacancy/{vacancyId}", DownstreamPathTemplate = "/api/v1/vacancy/{vacancyId}",
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 51879, {
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
},
UpstreamPathTemplate = "/vacancy/{vacancyId}", UpstreamPathTemplate = "/vacancy/{vacancyId}",
UpstreamHttpMethod = new List<string> { "Options", "Put", "Get", "Post", "Delete" }, UpstreamHttpMethod = new List<string> { "Options", "Put", "Get", "Post", "Delete" },
ServiceName = "botCore", ServiceName = "botCore",
@ -280,8 +346,14 @@ namespace Ocelot.AcceptanceTests
{ {
DownstreamPathTemplate = "/api/products", DownstreamPathTemplate = "/api/products",
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 51879, {
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
},
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
} }
@ -308,8 +380,14 @@ namespace Ocelot.AcceptanceTests
{ {
DownstreamPathTemplate = "/api/products", DownstreamPathTemplate = "/api/products",
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost/", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 51879, {
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
},
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
} }
@ -336,8 +414,14 @@ namespace Ocelot.AcceptanceTests
{ {
DownstreamPathTemplate = "/products", DownstreamPathTemplate = "/products",
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 51879, {
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
},
UpstreamPathTemplate = "/products/", UpstreamPathTemplate = "/products/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
} }
@ -364,8 +448,14 @@ namespace Ocelot.AcceptanceTests
{ {
DownstreamPathTemplate = "/products", DownstreamPathTemplate = "/products",
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 51879, {
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
},
UpstreamPathTemplate = "/products", UpstreamPathTemplate = "/products",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
} }
@ -391,8 +481,14 @@ namespace Ocelot.AcceptanceTests
{ {
DownstreamPathTemplate = "/products", DownstreamPathTemplate = "/products",
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 51879, {
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
},
UpstreamPathTemplate = "/products/{productId}", UpstreamPathTemplate = "/products/{productId}",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
QoSOptions = new FileQoSOptions() QoSOptions = new FileQoSOptions()
@ -424,8 +520,14 @@ namespace Ocelot.AcceptanceTests
{ {
DownstreamPathTemplate = "/api/products/{productId}", DownstreamPathTemplate = "/api/products/{productId}",
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 51879, {
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
},
UpstreamPathTemplate = "/products/{productId}", UpstreamPathTemplate = "/products/{productId}",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
} }
@ -453,8 +555,14 @@ namespace Ocelot.AcceptanceTests
{ {
DownstreamPathTemplate = "/api/{variantId}/products/{productId}", DownstreamPathTemplate = "/api/{variantId}/products/{productId}",
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 51879, {
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
},
UpstreamPathTemplate = "/{variantId}/products/{productId}", UpstreamPathTemplate = "/{variantId}/products/{productId}",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
} }
@ -482,8 +590,14 @@ namespace Ocelot.AcceptanceTests
{ {
DownstreamPathTemplate = "/api/products/{productId}", DownstreamPathTemplate = "/api/products/{productId}",
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 51879, {
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
},
UpstreamPathTemplate = "/products/{productId}", UpstreamPathTemplate = "/products/{productId}",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
} }
@ -508,8 +622,14 @@ namespace Ocelot.AcceptanceTests
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamHost = "localhost", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 51879, {
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
},
DownstreamScheme = "http", DownstreamScheme = "http",
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Post" }, UpstreamHttpMethod = new List<string> { "Post" },
@ -538,8 +658,14 @@ namespace Ocelot.AcceptanceTests
DownstreamPathTemplate = "/newThing", DownstreamPathTemplate = "/newThing",
UpstreamPathTemplate = "/newThing", UpstreamPathTemplate = "/newThing",
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 51879, {
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
},
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
} }
} }
@ -565,8 +691,14 @@ namespace Ocelot.AcceptanceTests
{ {
DownstreamPathTemplate = "/api/{urlPath}", DownstreamPathTemplate = "/api/{urlPath}",
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 51879, {
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
},
UpstreamPathTemplate = "/myApp1Name/api/{urlPath}", UpstreamPathTemplate = "/myApp1Name/api/{urlPath}",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
} }
@ -592,8 +724,14 @@ namespace Ocelot.AcceptanceTests
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamHost = "localhost", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 51879, {
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
},
DownstreamScheme = "http", DownstreamScheme = "http",
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get", "Post" }, UpstreamHttpMethod = new List<string> { "Get", "Post" },
@ -620,8 +758,14 @@ namespace Ocelot.AcceptanceTests
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamHost = "localhost", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 51879, {
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
},
DownstreamScheme = "http", DownstreamScheme = "http",
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string>(), UpstreamHttpMethod = new List<string>(),
@ -649,8 +793,14 @@ namespace Ocelot.AcceptanceTests
{ {
DownstreamPathTemplate = "/api/v1/vacancy", DownstreamPathTemplate = "/api/v1/vacancy",
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 51879, {
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
},
UpstreamPathTemplate = "/vacancy/", UpstreamPathTemplate = "/vacancy/",
UpstreamHttpMethod = new List<string> { "Options", "Put", "Get", "Post", "Delete" }, UpstreamHttpMethod = new List<string> { "Options", "Put", "Get", "Post", "Delete" },
ServiceName = "botCore", ServiceName = "botCore",
@ -660,8 +810,14 @@ namespace Ocelot.AcceptanceTests
{ {
DownstreamPathTemplate = "/api/v1/vacancy/{vacancyId}", DownstreamPathTemplate = "/api/v1/vacancy/{vacancyId}",
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 51879, {
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
},
UpstreamPathTemplate = "/vacancy/{vacancyId}", UpstreamPathTemplate = "/vacancy/{vacancyId}",
UpstreamHttpMethod = new List<string> { "Options", "Put", "Get", "Post", "Delete" }, UpstreamHttpMethod = new List<string> { "Options", "Put", "Get", "Post", "Delete" },
ServiceName = "botCore", ServiceName = "botCore",
@ -690,8 +846,14 @@ namespace Ocelot.AcceptanceTests
{ {
DownstreamPathTemplate = "/api/{url}", DownstreamPathTemplate = "/api/{url}",
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 51899, {
new FileHostAndPort
{
Host = "localhost",
Port = 51899,
}
},
UpstreamPathTemplate = "/platform/{url}", UpstreamPathTemplate = "/platform/{url}",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
QoSOptions = new FileQoSOptions { QoSOptions = new FileQoSOptions {

View File

@ -23,6 +23,8 @@ namespace Ocelot.AcceptanceTests
private int _counterOne; private int _counterOne;
private int _counterTwo; private int _counterTwo;
private static readonly object _syncLock = new object(); private static readonly object _syncLock = new object();
private IWebHost _builder;
private string _downstreamPath;
public ServiceDiscoveryTests() public ServiceDiscoveryTests()
{ {
@ -88,7 +90,7 @@ namespace Ocelot.AcceptanceTests
this.Given(x => x.GivenProductServiceOneIsRunning(downstreamServiceOneUrl, 200)) this.Given(x => x.GivenProductServiceOneIsRunning(downstreamServiceOneUrl, 200))
.And(x => x.GivenProductServiceTwoIsRunning(downstreamServiceTwoUrl, 200)) .And(x => x.GivenProductServiceTwoIsRunning(downstreamServiceTwoUrl, 200))
.And(x => x.GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl)) .And(x => x.GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, serviceName))
.And(x => x.GivenTheServicesAreRegisteredWithConsul(serviceEntryOne, serviceEntryTwo)) .And(x => x.GivenTheServicesAreRegisteredWithConsul(serviceEntryOne, serviceEntryTwo))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
@ -98,6 +100,62 @@ namespace Ocelot.AcceptanceTests
.BDDfy(); .BDDfy();
} }
//test from issue 213
[Fact]
public void should_handle_request_to_consul_for_downstream_service_and_make_request()
{
var consulPort = 8505;
var serviceName = "web";
var downstreamServiceOneUrl = "http://localhost:8080";
var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}";
var serviceEntryOne = new ServiceEntry()
{
Service = new AgentService()
{
Service = serviceName,
Address = "localhost",
Port = 8080,
ID = "web_90_0_2_224_8080",
Tags = new string[1]{"version-v1"}
},
};
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/api/home",
DownstreamScheme = "http",
UpstreamPathTemplate = "/home",
UpstreamHttpMethod = new List<string> { "Get", "Options" },
ServiceName = serviceName,
LoadBalancer = "LeastConnection",
UseServiceDiscovery = true,
}
},
GlobalConfiguration = new FileGlobalConfiguration()
{
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider()
{
Host = "localhost",
Port = consulPort
}
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn(downstreamServiceOneUrl, "/api/home", 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("/home"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.BDDfy();
}
[Fact] [Fact]
public void should_send_request_to_service_after_it_becomes_available() public void should_send_request_to_service_after_it_becomes_available()
{ {
@ -156,7 +214,7 @@ namespace Ocelot.AcceptanceTests
this.Given(x => x.GivenProductServiceOneIsRunning(downstreamServiceOneUrl, 200)) this.Given(x => x.GivenProductServiceOneIsRunning(downstreamServiceOneUrl, 200))
.And(x => x.GivenProductServiceTwoIsRunning(downstreamServiceTwoUrl, 200)) .And(x => x.GivenProductServiceTwoIsRunning(downstreamServiceTwoUrl, 200))
.And(x => x.GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl)) .And(x => x.GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, serviceName))
.And(x => x.GivenTheServicesAreRegisteredWithConsul(serviceEntryOne, serviceEntryTwo)) .And(x => x.GivenTheServicesAreRegisteredWithConsul(serviceEntryOne, serviceEntryTwo))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
@ -217,7 +275,7 @@ namespace Ocelot.AcceptanceTests
} }
} }
private void GivenThereIsAFakeConsulServiceDiscoveryProvider(string url) private void GivenThereIsAFakeConsulServiceDiscoveryProvider(string url, string serviceName)
{ {
_fakeConsulBuilder = new WebHostBuilder() _fakeConsulBuilder = new WebHostBuilder()
.UseUrls(url) .UseUrls(url)
@ -229,7 +287,7 @@ namespace Ocelot.AcceptanceTests
{ {
app.Run(async context => app.Run(async context =>
{ {
if(context.Request.Path.Value == "/v1/health/service/product") if(context.Request.Path.Value == $"/v1/health/service/{serviceName}")
{ {
await context.Response.WriteJsonAsync(_serviceEntries); await context.Response.WriteJsonAsync(_serviceEntries);
} }
@ -310,6 +368,37 @@ namespace Ocelot.AcceptanceTests
_builderTwo.Start(); _builderTwo.Start();
} }
private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody)
{
_builder = new WebHostBuilder()
.UseUrls(baseUrl)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.Configure(app =>
{
app.UsePathBase(basePath);
app.Run(async context =>
{
_downstreamPath = !string.IsNullOrEmpty(context.Request.PathBase.Value) ? context.Request.PathBase.Value : context.Request.Path.Value;
if(_downstreamPath != basePath)
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync("downstream path didnt match base path");
}
else
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody);
}
});
})
.Build();
_builder.Start();
}
public void Dispose() public void Dispose()
{ {
_builderOne?.Dispose(); _builderOne?.Dispose();

View File

@ -159,22 +159,28 @@ namespace Ocelot.AcceptanceTests
_ocelotClient = _ocelotServer.CreateClient(); _ocelotClient = _ocelotServer.CreateClient();
} }
internal void ThenTheResponseShouldBe(FileConfiguration expected) internal void ThenTheResponseShouldBe(FileConfiguration expecteds)
{ {
var response = JsonConvert.DeserializeObject<FileConfiguration>(_response.Content.ReadAsStringAsync().Result); var response = JsonConvert.DeserializeObject<FileConfiguration>(_response.Content.ReadAsStringAsync().Result);
response.GlobalConfiguration.RequestIdKey.ShouldBe(expected.GlobalConfiguration.RequestIdKey); response.GlobalConfiguration.RequestIdKey.ShouldBe(expecteds.GlobalConfiguration.RequestIdKey);
response.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Host); response.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expecteds.GlobalConfiguration.ServiceDiscoveryProvider.Host);
response.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Port); response.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(expecteds.GlobalConfiguration.ServiceDiscoveryProvider.Port);
for (var i = 0; i < response.ReRoutes.Count; i++) for (var i = 0; i < response.ReRoutes.Count; i++)
{ {
response.ReRoutes[i].DownstreamHost.ShouldBe(expected.ReRoutes[i].DownstreamHost); for (var j = 0; j < response.ReRoutes[i].DownstreamHostAndPorts.Count; j++)
response.ReRoutes[i].DownstreamPathTemplate.ShouldBe(expected.ReRoutes[i].DownstreamPathTemplate); {
response.ReRoutes[i].DownstreamPort.ShouldBe(expected.ReRoutes[i].DownstreamPort); var result = response.ReRoutes[i].DownstreamHostAndPorts[j];
response.ReRoutes[i].DownstreamScheme.ShouldBe(expected.ReRoutes[i].DownstreamScheme); var expected = expecteds.ReRoutes[i].DownstreamHostAndPorts[j];
response.ReRoutes[i].UpstreamPathTemplate.ShouldBe(expected.ReRoutes[i].UpstreamPathTemplate); result.Host.ShouldBe(expected.Host);
response.ReRoutes[i].UpstreamHttpMethod.ShouldBe(expected.ReRoutes[i].UpstreamHttpMethod); result.Port.ShouldBe(expected.Port);
}
response.ReRoutes[i].DownstreamPathTemplate.ShouldBe(expecteds.ReRoutes[i].DownstreamPathTemplate);
response.ReRoutes[i].DownstreamScheme.ShouldBe(expecteds.ReRoutes[i].DownstreamScheme);
response.ReRoutes[i].UpstreamPathTemplate.ShouldBe(expecteds.ReRoutes[i].UpstreamPathTemplate);
response.ReRoutes[i].UpstreamHttpMethod.ShouldBe(expecteds.ReRoutes[i].UpstreamHttpMethod);
} }
} }

View File

@ -37,57 +37,6 @@ namespace Ocelot.AcceptanceTests
var downstreamServiceTwoUrl = "http://localhost:8330"; var downstreamServiceTwoUrl = "http://localhost:8330";
var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}"; var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}";
// http://localhost:8362/api/user/info?id=1 is ok
// http://localhost:3164/api/user/info?id=1 is ok
// http://localhost:8330/api/product/info?id=1 is ok
// http://localhost:3164/api/product/info?id=1 is 404
// is my configuration.json
// {
// "ReRoutes": [
// //{
// // "DownstreamPathTemplate": "/{product}",
// // "DownstreamScheme": "http",
// // "UpstreamPathTemplate": "/{product}",
// // "UpstreamHttpMethod": [ "Get", "Post" ],
// // "ServiceName": "api-product",
// // "LoadBalancer": "LeastConnection",
// // "UseServiceDiscovery": true
// //},
// //{
// // "DownstreamPathTemplate": "/{user}",
// // "DownstreamScheme": "http",
// // "UpstreamPathTemplate": "/{user}",
// // "UpstreamHttpMethod": [ "Get", "Post" ],
// // "ServiceName": "api-user",
// // "LoadBalancer": "LeastConnection",
// // "UseServiceDiscovery": true
// //},
// {
// "DownstreamPathTemplate": "/api/user/{user}",
// "DownstreamScheme": "http",
// "DownstreamHost": "localhost",
// "DownstreamPort": 8362,
// "UpstreamPathTemplate": "/api/user/{user}",
// "UpstreamHttpMethod": [ "Get" ]
// },
// {
// "DownstreamPathTemplate": "/api/product/{product}",
// "DownstreamScheme": "http",
// "DownstreamHost": "localhost",
// "DownstreamPort": 8330,
// "UpstreamPathTemplate": "//api/product/{product}",
// "UpstreamHttpMethod": [ "Get" ]
// }
// ],
// "GlobalConfiguration": {
// "ServiceDiscoveryProvider": {
// "Host": "localhost",
// "Port": 8500
// }
// }
// }
var configuration = new FileConfiguration var configuration = new FileConfiguration
{ {
ReRoutes = new List<FileReRoute> ReRoutes = new List<FileReRoute>
@ -96,8 +45,14 @@ namespace Ocelot.AcceptanceTests
{ {
DownstreamPathTemplate = "/api/user/{user}", DownstreamPathTemplate = "/api/user/{user}",
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 8362, {
new FileHostAndPort
{
Host = "localhost",
Port = 8362,
}
},
UpstreamPathTemplate = "/api/user/{user}", UpstreamPathTemplate = "/api/user/{user}",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
}, },
@ -105,8 +60,14 @@ namespace Ocelot.AcceptanceTests
{ {
DownstreamPathTemplate = "/api/product/{product}", DownstreamPathTemplate = "/api/product/{product}",
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 8330, {
new FileHostAndPort
{
Host = "localhost",
Port = 8330,
}
},
UpstreamPathTemplate = "/api/product/{product}", UpstreamPathTemplate = "/api/product/{product}",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
} }

View File

@ -1 +1,59 @@
{"ReRoutes":[{"DownstreamPathTemplate":"41879/","UpstreamPathTemplate":"/","UpstreamHttpMethod":"Get","AuthenticationOptions":{"Provider":null,"ProviderRootUrl":null,"ApiName":null,"RequireHttps":false,"AllowedScopes":[],"ApiSecret":null},"AddHeadersToRequest":{},"AddClaimsToRequest":{},"RouteClaimsRequirement":{},"AddQueriesToRequest":{},"RequestIdKey":null,"FileCacheOptions":{"TtlSeconds":0},"ReRouteIsCaseSensitive":false,"ServiceName":null,"DownstreamScheme":"http","DownstreamHost":"localhost","DownstreamPort":41879,"QoSOptions":{"ExceptionsAllowedBeforeBreaking":0,"DurationOfBreak":0,"TimeoutValue":0},"LoadBalancer":null,"RateLimitOptions":{"ClientWhitelist":[],"EnableRateLimiting":false,"Period":null,"PeriodTimespan":0.0,"Limit":0}}],"GlobalConfiguration":{"RequestIdKey":null,"ServiceDiscoveryProvider":{"Provider":null,"Host":null,"Port":0},"AdministrationPath":null,"RateLimitOptions":{"ClientIdHeader":"ClientId","QuotaExceededMessage":null,"RateLimitCounterPrefix":"ocelot","DisableRateLimitHeaders":false,"HttpStatusCode":429}}} {
"ReRoutes": [
{
"DownstreamPathTemplate": "41879/",
"UpstreamPathTemplate": "/",
"UpstreamHttpMethod": "Get",
"AuthenticationOptions": {
"Provider": null,
"ProviderRootUrl": null,
"ApiName": null,
"RequireHttps": false,
"AllowedScopes": [],
"ApiSecret": null
},
"AddHeadersToRequest": {},
"AddClaimsToRequest": {},
"RouteClaimsRequirement": {},
"AddQueriesToRequest": {},
"RequestIdKey": null,
"FileCacheOptions": {
"TtlSeconds": 0
},
"ReRouteIsCaseSensitive": false,
"ServiceName": null,
"DownstreamScheme": "http",
"DownstreamHost": "localhost",
"DownstreamPort": 41879,
"QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 0,
"DurationOfBreak": 0,
"TimeoutValue": 0
},
"LoadBalancer": null,
"RateLimitOptions": {
"ClientWhitelist": [],
"EnableRateLimiting": false,
"Period": null,
"PeriodTimespan": 0,
"Limit": 0
}
}
],
"GlobalConfiguration": {
"RequestIdKey": null,
"ServiceDiscoveryProvider": {
"Provider": null,
"Host": null,
"Port": 0
},
"AdministrationPath": null,
"RateLimitOptions": {
"ClientIdHeader": "ClientId",
"QuotaExceededMessage": null,
"RateLimitCounterPrefix": "ocelot",
"DisableRateLimitHeaders": false,
"HttpStatusCode": 429
}
}
}

View File

@ -95,8 +95,14 @@ namespace Ocelot.IntegrationTests
{ {
new FileReRoute() new FileReRoute()
{ {
DownstreamHost = "localhost", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 80, {
new FileHostAndPort
{
Host = "localhost",
Port = 80,
}
},
DownstreamScheme = "https", DownstreamScheme = "https",
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "get" }, UpstreamHttpMethod = new List<string> { "get" },
@ -109,8 +115,14 @@ namespace Ocelot.IntegrationTests
}, },
new FileReRoute() new FileReRoute()
{ {
DownstreamHost = "localhost", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 80, {
new FileHostAndPort
{
Host = "localhost",
Port = 80,
}
},
DownstreamScheme = "https", DownstreamScheme = "https",
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "get" }, UpstreamHttpMethod = new List<string> { "get" },
@ -146,8 +158,14 @@ namespace Ocelot.IntegrationTests
{ {
new FileReRoute() new FileReRoute()
{ {
DownstreamHost = "localhost", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 80, {
new FileHostAndPort
{
Host = "localhost",
Port = 80,
}
},
DownstreamScheme = "https", DownstreamScheme = "https",
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "get" }, UpstreamHttpMethod = new List<string> { "get" },
@ -155,8 +173,14 @@ namespace Ocelot.IntegrationTests
}, },
new FileReRoute() new FileReRoute()
{ {
DownstreamHost = "localhost", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 80, {
new FileHostAndPort
{
Host = "localhost",
Port = 80,
}
},
DownstreamScheme = "https", DownstreamScheme = "https",
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "get" }, UpstreamHttpMethod = new List<string> { "get" },
@ -174,8 +198,14 @@ namespace Ocelot.IntegrationTests
{ {
new FileReRoute() new FileReRoute()
{ {
DownstreamHost = "127.0.0.1", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 80, {
new FileHostAndPort
{
Host = "localhost",
Port = 80,
}
},
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamPathTemplate = "/geoffrey", DownstreamPathTemplate = "/geoffrey",
UpstreamHttpMethod = new List<string> { "get" }, UpstreamHttpMethod = new List<string> { "get" },
@ -183,8 +213,14 @@ namespace Ocelot.IntegrationTests
}, },
new FileReRoute() new FileReRoute()
{ {
DownstreamHost = "123.123.123", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 443, {
new FileHostAndPort
{
Host = "123.123.123",
Port = 443,
}
},
DownstreamScheme = "https", DownstreamScheme = "https",
DownstreamPathTemplate = "/blooper/{productId}", DownstreamPathTemplate = "/blooper/{productId}",
UpstreamHttpMethod = new List<string> { "post" }, UpstreamHttpMethod = new List<string> { "post" },
@ -218,8 +254,14 @@ namespace Ocelot.IntegrationTests
{ {
new FileReRoute() new FileReRoute()
{ {
DownstreamHost = "localhost", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 80, {
new FileHostAndPort
{
Host = "localhost",
Port = 80,
}
},
DownstreamScheme = "https", DownstreamScheme = "https",
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "get" }, UpstreamHttpMethod = new List<string> { "get" },
@ -231,8 +273,14 @@ namespace Ocelot.IntegrationTests
}, },
new FileReRoute() new FileReRoute()
{ {
DownstreamHost = "localhost", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 80, {
new FileHostAndPort
{
Host = "localhost",
Port = 80,
}
},
DownstreamScheme = "https", DownstreamScheme = "https",
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "get" }, UpstreamHttpMethod = new List<string> { "get" },
@ -301,22 +349,28 @@ namespace Ocelot.IntegrationTests
result.Value.ShouldBe(expected); result.Value.ShouldBe(expected);
} }
private void ThenTheResponseShouldBe(FileConfiguration expected) private void ThenTheResponseShouldBe(FileConfiguration expecteds)
{ {
var response = JsonConvert.DeserializeObject<FileConfiguration>(_response.Content.ReadAsStringAsync().Result); var response = JsonConvert.DeserializeObject<FileConfiguration>(_response.Content.ReadAsStringAsync().Result);
response.GlobalConfiguration.RequestIdKey.ShouldBe(expected.GlobalConfiguration.RequestIdKey); response.GlobalConfiguration.RequestIdKey.ShouldBe(expecteds.GlobalConfiguration.RequestIdKey);
response.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Host); response.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expecteds.GlobalConfiguration.ServiceDiscoveryProvider.Host);
response.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Port); response.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(expecteds.GlobalConfiguration.ServiceDiscoveryProvider.Port);
for (var i = 0; i < response.ReRoutes.Count; i++) for (var i = 0; i < response.ReRoutes.Count; i++)
{ {
response.ReRoutes[i].DownstreamHost.ShouldBe(expected.ReRoutes[i].DownstreamHost); for (var j = 0; j < response.ReRoutes[i].DownstreamHostAndPorts.Count; j++)
response.ReRoutes[i].DownstreamPathTemplate.ShouldBe(expected.ReRoutes[i].DownstreamPathTemplate); {
response.ReRoutes[i].DownstreamPort.ShouldBe(expected.ReRoutes[i].DownstreamPort); var result = response.ReRoutes[i].DownstreamHostAndPorts[j];
response.ReRoutes[i].DownstreamScheme.ShouldBe(expected.ReRoutes[i].DownstreamScheme); var expected = expecteds.ReRoutes[i].DownstreamHostAndPorts[j];
response.ReRoutes[i].UpstreamPathTemplate.ShouldBe(expected.ReRoutes[i].UpstreamPathTemplate); result.Host.ShouldBe(expected.Host);
response.ReRoutes[i].UpstreamHttpMethod.ShouldBe(expected.ReRoutes[i].UpstreamHttpMethod); result.Port.ShouldBe(expected.Port);
}
response.ReRoutes[i].DownstreamPathTemplate.ShouldBe(expecteds.ReRoutes[i].DownstreamPathTemplate);
response.ReRoutes[i].DownstreamScheme.ShouldBe(expecteds.ReRoutes[i].DownstreamScheme);
response.ReRoutes[i].UpstreamPathTemplate.ShouldBe(expecteds.ReRoutes[i].UpstreamPathTemplate);
response.ReRoutes[i].UpstreamHttpMethod.ShouldBe(expecteds.ReRoutes[i].UpstreamHttpMethod);
} }
} }

View File

@ -5,16 +5,13 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Net.Http.Headers; using System.Net.Http.Headers;
using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json; using Newtonsoft.Json;
using Ocelot.Configuration.File; using Ocelot.Configuration.File;
using Ocelot.Raft; using Ocelot.Raft;
using Rafty.Concensus; using Rafty.Concensus;
using Rafty.FiniteStateMachine;
using Rafty.Infrastructure; using Rafty.Infrastructure;
using Shouldly; using Shouldly;
using Xunit; using Xunit;
@ -25,16 +22,16 @@ namespace Ocelot.IntegrationTests
{ {
public class RaftTests : IDisposable public class RaftTests : IDisposable
{ {
private List<IWebHost> _builders; private readonly List<IWebHost> _builders;
private List<IWebHostBuilder> _webHostBuilders; private readonly List<IWebHostBuilder> _webHostBuilders;
private List<Thread> _threads; private readonly List<Thread> _threads;
private FilePeers _peers; private FilePeers _peers;
private HttpClient _httpClient; private readonly HttpClient _httpClient;
private HttpClient _httpClientForAssertions; private readonly HttpClient _httpClientForAssertions;
private string _ocelotBaseUrl; private string _ocelotBaseUrl;
private BearerToken _token; private BearerToken _token;
private HttpResponseMessage _response; private HttpResponseMessage _response;
private static object _lock = new object(); private static readonly object _lock = new object();
public RaftTests() public RaftTests()
{ {
@ -79,8 +76,14 @@ namespace Ocelot.IntegrationTests
{ {
new FileReRoute() new FileReRoute()
{ {
DownstreamHost = "127.0.0.1", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 80, {
new FileHostAndPort
{
Host = "127.0.0.1",
Port = 80,
}
},
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamPathTemplate = "/geoffrey", DownstreamPathTemplate = "/geoffrey",
UpstreamHttpMethod = new List<string> { "get" }, UpstreamHttpMethod = new List<string> { "get" },
@ -88,8 +91,14 @@ namespace Ocelot.IntegrationTests
}, },
new FileReRoute() new FileReRoute()
{ {
DownstreamHost = "123.123.123", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 443, {
new FileHostAndPort
{
Host = "123.123.123",
Port = 443,
}
},
DownstreamScheme = "https", DownstreamScheme = "https",
DownstreamPathTemplate = "/blooper/{productId}", DownstreamPathTemplate = "/blooper/{productId}",
UpstreamHttpMethod = new List<string> { "post" }, UpstreamHttpMethod = new List<string> { "post" },
@ -120,8 +129,14 @@ namespace Ocelot.IntegrationTests
{ {
new FileReRoute() new FileReRoute()
{ {
DownstreamHost = "127.0.0.1", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 80, {
new FileHostAndPort
{
Host = "127.0.0.1",
Port = 80,
}
},
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamPathTemplate = "/geoffrey", DownstreamPathTemplate = "/geoffrey",
UpstreamHttpMethod = new List<string> { "get" }, UpstreamHttpMethod = new List<string> { "get" },
@ -129,8 +144,14 @@ namespace Ocelot.IntegrationTests
}, },
new FileReRoute() new FileReRoute()
{ {
DownstreamHost = "123.123.123", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 443, {
new FileHostAndPort
{
Host = "123.123.123",
Port = 443,
}
},
DownstreamScheme = "https", DownstreamScheme = "https",
DownstreamPathTemplate = "/blooper/{productId}", DownstreamPathTemplate = "/blooper/{productId}",
UpstreamHttpMethod = new List<string> { "post" }, UpstreamHttpMethod = new List<string> { "post" },
@ -175,7 +196,7 @@ namespace Ocelot.IntegrationTests
} }
} }
private void ThenTheCommandIsReplicatedToAllStateMachines(UpdateFileConfiguration expected) private void ThenTheCommandIsReplicatedToAllStateMachines(UpdateFileConfiguration expecteds)
{ {
//dirty sleep to give a chance to replicate... //dirty sleep to give a chance to replicate...
var stopwatch = Stopwatch.StartNew(); var stopwatch = Stopwatch.StartNew();
@ -206,18 +227,24 @@ namespace Ocelot.IntegrationTests
var result = _httpClientForAssertions.GetAsync($"{peer.HostAndPort}/administration/configuration").Result; var result = _httpClientForAssertions.GetAsync($"{peer.HostAndPort}/administration/configuration").Result;
var json = result.Content.ReadAsStringAsync().Result; var json = result.Content.ReadAsStringAsync().Result;
var response = JsonConvert.DeserializeObject<FileConfiguration>(json, new JsonSerializerSettings{TypeNameHandling = TypeNameHandling.All}); var response = JsonConvert.DeserializeObject<FileConfiguration>(json, new JsonSerializerSettings{TypeNameHandling = TypeNameHandling.All});
response.GlobalConfiguration.RequestIdKey.ShouldBe(expected.Configuration.GlobalConfiguration.RequestIdKey); response.GlobalConfiguration.RequestIdKey.ShouldBe(expecteds.Configuration.GlobalConfiguration.RequestIdKey);
response.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expected.Configuration.GlobalConfiguration.ServiceDiscoveryProvider.Host); response.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expecteds.Configuration.GlobalConfiguration.ServiceDiscoveryProvider.Host);
response.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(expected.Configuration.GlobalConfiguration.ServiceDiscoveryProvider.Port); response.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(expecteds.Configuration.GlobalConfiguration.ServiceDiscoveryProvider.Port);
for (var i = 0; i < response.ReRoutes.Count; i++) for (var i = 0; i < response.ReRoutes.Count; i++)
{ {
response.ReRoutes[i].DownstreamHost.ShouldBe(expected.Configuration.ReRoutes[i].DownstreamHost); for (var j = 0; j < response.ReRoutes[i].DownstreamHostAndPorts.Count; j++)
response.ReRoutes[i].DownstreamPathTemplate.ShouldBe(expected.Configuration.ReRoutes[i].DownstreamPathTemplate); {
response.ReRoutes[i].DownstreamPort.ShouldBe(expected.Configuration.ReRoutes[i].DownstreamPort); var res = response.ReRoutes[i].DownstreamHostAndPorts[j];
response.ReRoutes[i].DownstreamScheme.ShouldBe(expected.Configuration.ReRoutes[i].DownstreamScheme); var expected = expecteds.Configuration.ReRoutes[i].DownstreamHostAndPorts[j];
response.ReRoutes[i].UpstreamPathTemplate.ShouldBe(expected.Configuration.ReRoutes[i].UpstreamPathTemplate); res.Host.ShouldBe(expected.Host);
response.ReRoutes[i].UpstreamHttpMethod.ShouldBe(expected.Configuration.ReRoutes[i].UpstreamHttpMethod); res.Port.ShouldBe(expected.Port);
}
response.ReRoutes[i].DownstreamPathTemplate.ShouldBe(expecteds.Configuration.ReRoutes[i].DownstreamPathTemplate);
response.ReRoutes[i].DownstreamScheme.ShouldBe(expecteds.Configuration.ReRoutes[i].DownstreamScheme);
response.ReRoutes[i].UpstreamPathTemplate.ShouldBe(expecteds.Configuration.ReRoutes[i].UpstreamPathTemplate);
response.ReRoutes[i].UpstreamHttpMethod.ShouldBe(expecteds.Configuration.ReRoutes[i].UpstreamHttpMethod);
} }
passed++; passed++;
} }
@ -235,30 +262,6 @@ namespace Ocelot.IntegrationTests
commandOnAllStateMachines.ShouldBeTrue(); commandOnAllStateMachines.ShouldBeTrue();
} }
private void ThenTheResponseShouldBe(FileConfiguration expected)
{
var response = JsonConvert.DeserializeObject<FileConfiguration>(_response.Content.ReadAsStringAsync().Result);
response.GlobalConfiguration.RequestIdKey.ShouldBe(expected.GlobalConfiguration.RequestIdKey);
response.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Host);
response.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Port);
for (var i = 0; i < response.ReRoutes.Count; i++)
{
response.ReRoutes[i].DownstreamHost.ShouldBe(expected.ReRoutes[i].DownstreamHost);
response.ReRoutes[i].DownstreamPathTemplate.ShouldBe(expected.ReRoutes[i].DownstreamPathTemplate);
response.ReRoutes[i].DownstreamPort.ShouldBe(expected.ReRoutes[i].DownstreamPort);
response.ReRoutes[i].DownstreamScheme.ShouldBe(expected.ReRoutes[i].DownstreamScheme);
response.ReRoutes[i].UpstreamPathTemplate.ShouldBe(expected.ReRoutes[i].UpstreamPathTemplate);
response.ReRoutes[i].UpstreamHttpMethod.ShouldBe(expected.ReRoutes[i].UpstreamHttpMethod);
}
}
private void WhenIGetUrlOnTheApiGateway(string url)
{
_response = _httpClient.GetAsync(url).Result;
}
private void WhenIPostOnTheApiGateway(string url, FileConfiguration updatedConfiguration) private void WhenIPostOnTheApiGateway(string url, FileConfiguration updatedConfiguration)
{ {
var json = JsonConvert.SerializeObject(updatedConfiguration); var json = JsonConvert.SerializeObject(updatedConfiguration);
@ -365,67 +368,5 @@ namespace Ocelot.IntegrationTests
} }
} }
private void WhenISendACommandIntoTheCluster(FakeCommand command)
{
var p = _peers.Peers.First();
var json = JsonConvert.SerializeObject(command,new JsonSerializerSettings() {
TypeNameHandling = TypeNameHandling.All
});
var httpContent = new StringContent(json);
httpContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
using(var httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token.AccessToken);
var response = httpClient.PostAsync($"{p.HostAndPort}/administration/raft/command", httpContent).GetAwaiter().GetResult();
response.EnsureSuccessStatusCode();
var content = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
var result = JsonConvert.DeserializeObject<OkResponse<FakeCommand>>(content);
result.Command.Value.ShouldBe(command.Value);
}
//dirty sleep to make sure command replicated...
var stopwatch = Stopwatch.StartNew();
while(stopwatch.ElapsedMilliseconds < 10000)
{
}
}
private void ThenTheCommandIsReplicatedToAllStateMachines(FakeCommand command)
{
//dirty sleep to give a chance to replicate...
var stopwatch = Stopwatch.StartNew();
while(stopwatch.ElapsedMilliseconds < 2000)
{
}
bool CommandCalledOnAllStateMachines()
{
try
{
var passed = 0;
foreach (var peer in _peers.Peers)
{
string fsmData;
fsmData = File.ReadAllText(peer.HostAndPort.Replace("/","").Replace(":",""));
fsmData.ShouldNotBeNullOrEmpty();
var fakeCommand = JsonConvert.DeserializeObject<FakeCommand>(fsmData);
fakeCommand.Value.ShouldBe(command.Value);
passed++;
}
return passed == 5;
}
catch(Exception e)
{
return false;
}
}
var commandOnAllStateMachines = WaitFor(20000).Until(() => CommandCalledOnAllStateMachines());
commandOnAllStateMachines.ShouldBeTrue();
}
} }
} }

View File

@ -46,8 +46,14 @@ namespace Ocelot.IntegrationTests
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 51879, {
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
},
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
} }

View File

@ -3,8 +3,12 @@
{ {
"DownstreamPathTemplate": "/", "DownstreamPathTemplate": "/",
"DownstreamScheme": "http", "DownstreamScheme": "http",
"DownstreamHost": "localhost", "DownstreamHostAndPorts": [
"DownstreamPort": 52876, {
"Host": "localhost",
"Port": 52876
}
],
"UpstreamPathTemplate": "/identityserverexample", "UpstreamPathTemplate": "/identityserverexample",
"UpstreamHttpMethod": [ "Get" ], "UpstreamHttpMethod": [ "Get" ],
"QoSOptions": { "QoSOptions": {
@ -45,8 +49,12 @@
{ {
"DownstreamPathTemplate": "/posts", "DownstreamPathTemplate": "/posts",
"DownstreamScheme": "https", "DownstreamScheme": "https",
"DownstreamHost": "jsonplaceholder.typicode.com", "DownstreamHostAndPorts": [
"DownstreamPort": 443, {
"Host": "jsonplaceholder.typicode.com",
"Port": 443
}
],
"UpstreamPathTemplate": "/posts", "UpstreamPathTemplate": "/posts",
"UpstreamHttpMethod": [ "Get" ], "UpstreamHttpMethod": [ "Get" ],
"QoSOptions": { "QoSOptions": {
@ -58,8 +66,12 @@
{ {
"DownstreamPathTemplate": "/posts/{postId}", "DownstreamPathTemplate": "/posts/{postId}",
"DownstreamScheme": "http", "DownstreamScheme": "http",
"DownstreamHost": "jsonplaceholder.typicode.com", "DownstreamHostAndPorts": [
"DownstreamPort": 80, {
"Host": "jsonplaceholder.typicode.com",
"Port": 80
}
],
"UpstreamPathTemplate": "/posts/{postId}", "UpstreamPathTemplate": "/posts/{postId}",
"UpstreamHttpMethod": [ "Get" ], "UpstreamHttpMethod": [ "Get" ],
"RequestIdKey": "ReRouteRequestId", "RequestIdKey": "ReRouteRequestId",
@ -72,8 +84,12 @@
{ {
"DownstreamPathTemplate": "/posts/{postId}/comments", "DownstreamPathTemplate": "/posts/{postId}/comments",
"DownstreamScheme": "http", "DownstreamScheme": "http",
"DownstreamHost": "jsonplaceholder.typicode.com", "DownstreamHostAndPorts": [
"DownstreamPort": 80, {
"Host": "jsonplaceholder.typicode.com",
"Port": 80
}
],
"UpstreamPathTemplate": "/posts/{postId}/comments", "UpstreamPathTemplate": "/posts/{postId}/comments",
"UpstreamHttpMethod": [ "Get" ], "UpstreamHttpMethod": [ "Get" ],
"QoSOptions": { "QoSOptions": {
@ -85,8 +101,12 @@
{ {
"DownstreamPathTemplate": "/comments", "DownstreamPathTemplate": "/comments",
"DownstreamScheme": "http", "DownstreamScheme": "http",
"DownstreamHost": "jsonplaceholder.typicode.com", "DownstreamHostAndPorts": [
"DownstreamPort": 80, {
"Host": "jsonplaceholder.typicode.com",
"Port": 80
}
],
"UpstreamPathTemplate": "/comments", "UpstreamPathTemplate": "/comments",
"UpstreamHttpMethod": [ "Get" ], "UpstreamHttpMethod": [ "Get" ],
"QoSOptions": { "QoSOptions": {
@ -98,8 +118,12 @@
{ {
"DownstreamPathTemplate": "/posts", "DownstreamPathTemplate": "/posts",
"DownstreamScheme": "http", "DownstreamScheme": "http",
"DownstreamHost": "jsonplaceholder.typicode.com", "DownstreamHostAndPorts": [
"DownstreamPort": 80, {
"Host": "jsonplaceholder.typicode.com",
"Port": 80
}
],
"UpstreamPathTemplate": "/posts", "UpstreamPathTemplate": "/posts",
"UpstreamHttpMethod": [ "Post" ], "UpstreamHttpMethod": [ "Post" ],
"QoSOptions": { "QoSOptions": {
@ -111,8 +135,12 @@
{ {
"DownstreamPathTemplate": "/posts/{postId}", "DownstreamPathTemplate": "/posts/{postId}",
"DownstreamScheme": "http", "DownstreamScheme": "http",
"DownstreamHost": "jsonplaceholder.typicode.com", "DownstreamHostAndPorts": [
"DownstreamPort": 80, {
"Host": "jsonplaceholder.typicode.com",
"Port": 80
}
],
"UpstreamPathTemplate": "/posts/{postId}", "UpstreamPathTemplate": "/posts/{postId}",
"UpstreamHttpMethod": [ "Put" ], "UpstreamHttpMethod": [ "Put" ],
"QoSOptions": { "QoSOptions": {
@ -124,8 +152,12 @@
{ {
"DownstreamPathTemplate": "/posts/{postId}", "DownstreamPathTemplate": "/posts/{postId}",
"DownstreamScheme": "http", "DownstreamScheme": "http",
"DownstreamHost": "jsonplaceholder.typicode.com", "DownstreamHostAndPorts": [
"DownstreamPort": 80, {
"Host": "jsonplaceholder.typicode.com",
"Port": 80
}
],
"UpstreamPathTemplate": "/posts/{postId}", "UpstreamPathTemplate": "/posts/{postId}",
"UpstreamHttpMethod": [ "Patch" ], "UpstreamHttpMethod": [ "Patch" ],
"QoSOptions": { "QoSOptions": {
@ -137,8 +169,12 @@
{ {
"DownstreamPathTemplate": "/posts/{postId}", "DownstreamPathTemplate": "/posts/{postId}",
"DownstreamScheme": "http", "DownstreamScheme": "http",
"DownstreamHost": "jsonplaceholder.typicode.com", "DownstreamHostAndPorts": [
"DownstreamPort": 80, {
"Host": "jsonplaceholder.typicode.com",
"Port": 80
}
],
"UpstreamPathTemplate": "/posts/{postId}", "UpstreamPathTemplate": "/posts/{postId}",
"UpstreamHttpMethod": [ "Delete" ], "UpstreamHttpMethod": [ "Delete" ],
"QoSOptions": { "QoSOptions": {
@ -150,8 +186,12 @@
{ {
"DownstreamPathTemplate": "/api/products", "DownstreamPathTemplate": "/api/products",
"DownstreamScheme": "http", "DownstreamScheme": "http",
"DownstreamHost": "jsonplaceholder.typicode.com", "DownstreamHostAndPorts": [
"DownstreamPort": 80, {
"Host": "jsonplaceholder.typicode.com",
"Port": 80
}
],
"UpstreamPathTemplate": "/products", "UpstreamPathTemplate": "/products",
"UpstreamHttpMethod": [ "Get" ], "UpstreamHttpMethod": [ "Get" ],
"QoSOptions": { "QoSOptions": {
@ -164,8 +204,12 @@
{ {
"DownstreamPathTemplate": "/api/products/{productId}", "DownstreamPathTemplate": "/api/products/{productId}",
"DownstreamScheme": "http", "DownstreamScheme": "http",
"DownstreamHost": "jsonplaceholder.typicode.com", "DownstreamHostAndPorts": [
"DownstreamPort": 80, {
"Host": "jsonplaceholder.typicode.com",
"Port": 80
}
],
"UpstreamPathTemplate": "/products/{productId}", "UpstreamPathTemplate": "/products/{productId}",
"UpstreamHttpMethod": [ "Get" ], "UpstreamHttpMethod": [ "Get" ],
"FileCacheOptions": { "TtlSeconds": 15 } "FileCacheOptions": { "TtlSeconds": 15 }
@ -173,8 +217,12 @@
{ {
"DownstreamPathTemplate": "/api/products", "DownstreamPathTemplate": "/api/products",
"DownstreamScheme": "http", "DownstreamScheme": "http",
"DownstreamHost": "products20161126090340.azurewebsites.net", "DownstreamHostAndPorts": [
"DownstreamPort": 80, {
"Host": "jsonplaceholder.typicode.com",
"Port": 80
}
],
"UpstreamPathTemplate": "/products", "UpstreamPathTemplate": "/products",
"UpstreamHttpMethod": [ "Post" ], "UpstreamHttpMethod": [ "Post" ],
"QoSOptions": { "QoSOptions": {
@ -186,8 +234,12 @@
{ {
"DownstreamPathTemplate": "/api/products/{productId}", "DownstreamPathTemplate": "/api/products/{productId}",
"DownstreamScheme": "http", "DownstreamScheme": "http",
"DownstreamHost": "products20161126090340.azurewebsites.net", "DownstreamHostAndPorts": [
"DownstreamPort": 80, {
"Host": "jsonplaceholder.typicode.com",
"Port": 80
}
],
"UpstreamPathTemplate": "/products/{productId}", "UpstreamPathTemplate": "/products/{productId}",
"UpstreamHttpMethod": [ "Put" ], "UpstreamHttpMethod": [ "Put" ],
"QoSOptions": { "QoSOptions": {
@ -197,95 +249,15 @@
}, },
"FileCacheOptions": { "TtlSeconds": 15 } "FileCacheOptions": { "TtlSeconds": 15 }
}, },
{
"DownstreamPathTemplate": "/api/products/{productId}",
"DownstreamScheme": "http",
"DownstreamHost": "products20161126090340.azurewebsites.net",
"DownstreamPort": 80,
"UpstreamPathTemplate": "/products/{productId}",
"UpstreamHttpMethod": [ "Delete" ],
"QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 3,
"DurationOfBreak": 10,
"TimeoutValue": 5000
},
"FileCacheOptions": { "TtlSeconds": 15 }
},
{
"DownstreamPathTemplate": "/api/customers",
"DownstreamScheme": "http",
"DownstreamHost": "customers20161126090811.azurewebsites.net",
"DownstreamPort": 80,
"UpstreamPathTemplate": "/customers",
"UpstreamHttpMethod": [ "Get" ],
"QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 3,
"DurationOfBreak": 10,
"TimeoutValue": 5000
},
"FileCacheOptions": { "TtlSeconds": 15 }
},
{
"DownstreamPathTemplate": "/api/customers/{customerId}",
"DownstreamScheme": "http",
"DownstreamHost": "customers20161126090811.azurewebsites.net",
"DownstreamPort": 80,
"UpstreamPathTemplate": "/customers/{customerId}",
"UpstreamHttpMethod": [ "Get" ],
"QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 3,
"DurationOfBreak": 10,
"TimeoutValue": 5000
},
"FileCacheOptions": { "TtlSeconds": 15 }
},
{
"DownstreamPathTemplate": "/api/customers",
"DownstreamScheme": "http",
"DownstreamHost": "customers20161126090811.azurewebsites.net",
"DownstreamPort": 80,
"UpstreamPathTemplate": "/customers",
"UpstreamHttpMethod": [ "Post" ],
"QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 3,
"DurationOfBreak": 10,
"TimeoutValue": 5000
},
"FileCacheOptions": { "TtlSeconds": 15 }
},
{
"DownstreamPathTemplate": "/api/customers/{customerId}",
"DownstreamScheme": "http",
"DownstreamHost": "customers20161126090811.azurewebsites.net",
"DownstreamPort": 80,
"UpstreamPathTemplate": "/customers/{customerId}",
"UpstreamHttpMethod": [ "Put" ],
"QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 3,
"DurationOfBreak": 10,
"TimeoutValue": 5000
},
"FileCacheOptions": { "TtlSeconds": 15 }
},
{
"DownstreamPathTemplate": "/api/customers/{customerId}",
"DownstreamScheme": "http",
"DownstreamHost": "customers20161126090811.azurewebsites.net",
"DownstreamPort": 80,
"UpstreamPathTemplate": "/customers/{customerId}",
"UpstreamHttpMethod": [ "Delete" ],
"QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 3,
"DurationOfBreak": 10,
"TimeoutValue": 5000
},
"FileCacheOptions": { "TtlSeconds": 15 }
},
{ {
"DownstreamPathTemplate": "/posts", "DownstreamPathTemplate": "/posts",
"DownstreamScheme": "http", "DownstreamScheme": "http",
"DownstreamHost": "jsonplaceholder.typicode.com", "DownstreamHostAndPorts": [
"DownstreamPort": 80, {
"Host": "jsonplaceholder.typicode.com",
"Port": 80
}
],
"UpstreamPathTemplate": "/posts/", "UpstreamPathTemplate": "/posts/",
"UpstreamHttpMethod": [ "Get" ], "UpstreamHttpMethod": [ "Get" ],
"QoSOptions": { "QoSOptions": {
@ -298,8 +270,12 @@
{ {
"DownstreamPathTemplate": "/", "DownstreamPathTemplate": "/",
"DownstreamScheme": "http", "DownstreamScheme": "http",
"DownstreamHost": "www.bbc.co.uk", "DownstreamHostAndPorts": [
"DownstreamPort": 80, {
"Host": "www.bbc.co.uk",
"Port": 80
}
],
"UpstreamPathTemplate": "/bbc/", "UpstreamPathTemplate": "/bbc/",
"UpstreamHttpMethod": [ "Get" ] "UpstreamHttpMethod": [ "Get" ]
} }

View File

@ -65,7 +65,13 @@ namespace Ocelot.UnitTests.Configuration
{ {
DownstreamPathTemplate = "/api/products/", DownstreamPathTemplate = "/api/products/",
UpstreamPathTemplate = "/asdf/", UpstreamPathTemplate = "/asdf/",
DownstreamHost = "bbc.co.uk" DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "bbc.co.uk"
}
},
} }
} }
})) }))
@ -125,8 +131,14 @@ namespace Ocelot.UnitTests.Configuration
{ {
DownstreamPathTemplate = "/api/products/", DownstreamPathTemplate = "/api/products/",
UpstreamPathTemplate = "//api/prod/", UpstreamPathTemplate = "//api/prod/",
DownstreamHost = "bbc.co.uk", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 80 {
new FileHostAndPort
{
Host = "bbc.co.uk",
Port = 80
}
},
} }
} }
})) }))
@ -147,8 +159,14 @@ namespace Ocelot.UnitTests.Configuration
{ {
DownstreamPathTemplate = "//api/products/", DownstreamPathTemplate = "//api/products/",
UpstreamPathTemplate = "/api/prod/", UpstreamPathTemplate = "/api/prod/",
DownstreamHost = "bbc.co.uk", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 80 {
new FileHostAndPort
{
Host = "bbc.co.uk",
Port = 80
}
},
} }
} }
})) }))
@ -169,7 +187,13 @@ namespace Ocelot.UnitTests.Configuration
{ {
DownstreamPathTemplate = "/api/products/", DownstreamPathTemplate = "/api/products/",
UpstreamPathTemplate = "/asdf/", UpstreamPathTemplate = "/asdf/",
DownstreamHost = "bbc.co.uk", DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "bbc.co.uk",
}
},
AuthenticationOptions = new FileAuthenticationOptions() AuthenticationOptions = new FileAuthenticationOptions()
{ {
AuthenticationProviderKey = "Test" AuthenticationProviderKey = "Test"
@ -217,13 +241,25 @@ namespace Ocelot.UnitTests.Configuration
{ {
DownstreamPathTemplate = "/api/products/", DownstreamPathTemplate = "/api/products/",
UpstreamPathTemplate = "/asdf/", UpstreamPathTemplate = "/asdf/",
DownstreamHost = "bb.co.uk" DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "bb.co.uk"
}
},
}, },
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/www/test/", DownstreamPathTemplate = "/www/test/",
UpstreamPathTemplate = "/asdf/", UpstreamPathTemplate = "/asdf/",
DownstreamHost = "bb.co.uk" DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "bb.co.uk"
}
},
} }
} }
})) }))
@ -244,14 +280,26 @@ namespace Ocelot.UnitTests.Configuration
{ {
DownstreamPathTemplate = "/api/products/", DownstreamPathTemplate = "/api/products/",
UpstreamPathTemplate = "/asdf/", UpstreamPathTemplate = "/asdf/",
DownstreamHost = "bbc.co.uk", DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "bbc.co.uk",
}
},
UpstreamHttpMethod = new List<string> {"Get"} UpstreamHttpMethod = new List<string> {"Get"}
}, },
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/www/test/", DownstreamPathTemplate = "/www/test/",
UpstreamPathTemplate = "/asdf/", UpstreamPathTemplate = "/asdf/",
DownstreamHost = "bbc.co.uk", DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "bbc.co.uk",
}
},
UpstreamHttpMethod = new List<string> {"Get"} UpstreamHttpMethod = new List<string> {"Get"}
} }
} }
@ -274,14 +322,26 @@ namespace Ocelot.UnitTests.Configuration
DownstreamPathTemplate = "/api/products/", DownstreamPathTemplate = "/api/products/",
UpstreamPathTemplate = "/asdf/", UpstreamPathTemplate = "/asdf/",
UpstreamHttpMethod = new List<string> {"Get"}, UpstreamHttpMethod = new List<string> {"Get"},
DownstreamHost = "bbc.co.uk", DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "bbc.co.uk",
}
},
}, },
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/www/test/", DownstreamPathTemplate = "/www/test/",
UpstreamPathTemplate = "/asdf/", UpstreamPathTemplate = "/asdf/",
UpstreamHttpMethod = new List<string> {"Post"}, UpstreamHttpMethod = new List<string> {"Post"},
DownstreamHost = "bbc.co.uk", DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "bbc.co.uk",
}
},
} }
} }
})) }))
@ -302,7 +362,13 @@ namespace Ocelot.UnitTests.Configuration
DownstreamPathTemplate = "/api/products/", DownstreamPathTemplate = "/api/products/",
UpstreamPathTemplate = "/asdf/", UpstreamPathTemplate = "/asdf/",
UpstreamHttpMethod = new List<string> {"Get"}, UpstreamHttpMethod = new List<string> {"Get"},
DownstreamHost = "bbc.co.uk", DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "bbc.co.uk",
}
},
RateLimitOptions = new FileRateLimitRule RateLimitOptions = new FileRateLimitRule
{ {
Period = "1x", Period = "1x",
@ -329,7 +395,13 @@ namespace Ocelot.UnitTests.Configuration
DownstreamPathTemplate = "/api/products/", DownstreamPathTemplate = "/api/products/",
UpstreamPathTemplate = "/asdf/", UpstreamPathTemplate = "/asdf/",
UpstreamHttpMethod = new List<string> {"Get"}, UpstreamHttpMethod = new List<string> {"Get"},
DownstreamHost = "bbc.co.uk", DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "bbc.co.uk",
}
},
RateLimitOptions = new FileRateLimitRule RateLimitOptions = new FileRateLimitRule
{ {
Period = "1d", Period = "1d",
@ -406,13 +478,19 @@ namespace Ocelot.UnitTests.Configuration
UpstreamPathTemplate = "/asdf/", UpstreamPathTemplate = "/asdf/",
UpstreamHttpMethod = new List<string> {"Get"}, UpstreamHttpMethod = new List<string> {"Get"},
UseServiceDiscovery = false, UseServiceDiscovery = false,
DownstreamHost = downstreamHost DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = downstreamHost,
}
},
} }
} }
})) }))
.When(x => x.WhenIValidateTheConfiguration()) .When(x => x.WhenIValidateTheConfiguration())
.Then(x => x.ThenTheResultIsNotValid()) .Then(x => x.ThenTheResultIsNotValid())
.And(x => x.ThenTheErrorMessageAtPositionIs(0, "When not using service discover DownstreamHost must be set or Ocelot cannot find your service!")) .And(x => x.ThenTheErrorMessageAtPositionIs(0, "When not using service discovery Host must be set on DownstreamHostAndPorts if you are not using ReRoute.Host or Ocelot cannot find your service!"))
.BDDfy(); .BDDfy();
} }
@ -429,7 +507,13 @@ namespace Ocelot.UnitTests.Configuration
UpstreamPathTemplate = "/asdf/", UpstreamPathTemplate = "/asdf/",
UpstreamHttpMethod = new List<string> {"Get"}, UpstreamHttpMethod = new List<string> {"Get"},
UseServiceDiscovery = false, UseServiceDiscovery = false,
DownstreamHost = "bbc.co.uk" DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "bbc.co.uk"
}
},
} }
} }
})) }))
@ -438,6 +522,84 @@ namespace Ocelot.UnitTests.Configuration
.BDDfy(); .BDDfy();
} }
[Fact]
public void configuration_is_valid_when_no_downstream_but_has_host_and_port()
{
this.Given(x => x.GivenAConfiguration(new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/api/products/",
UpstreamPathTemplate = "/asdf/",
UpstreamHttpMethod = new List<string> {"Get"},
UseServiceDiscovery = false,
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "test"
}
}
}
}
}))
.When(x => x.WhenIValidateTheConfiguration())
.Then(x => x.ThenTheResultIsValid())
.BDDfy();
}
[Fact]
public void configuration_is_not_valid_when_no_host_and_port()
{
this.Given(x => x.GivenAConfiguration(new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/api/products/",
UpstreamPathTemplate = "/asdf/",
UpstreamHttpMethod = new List<string> {"Get"},
UseServiceDiscovery = false,
DownstreamHostAndPorts = new List<FileHostAndPort>
{
}
}
}
}))
.When(x => x.WhenIValidateTheConfiguration())
.Then(x => x.ThenTheResultIsNotValid())
.And(x => x.ThenTheErrorMessageAtPositionIs(0, "When not using service discovery DownstreamHostAndPorts must be set and not empty or Ocelot cannot find your service!"))
.BDDfy();
}
[Fact]
public void configuration_is_not_valid_when_host_and_port_is_empty()
{
this.Given(x => x.GivenAConfiguration(new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/api/products/",
UpstreamPathTemplate = "/asdf/",
UpstreamHttpMethod = new List<string> {"Get"},
UseServiceDiscovery = false,
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort()
}
}
}
}))
.When(x => x.WhenIValidateTheConfiguration())
.Then(x => x.ThenTheResultIsNotValid())
.And(x => x.ThenTheErrorMessageAtPositionIs(0, "When not using service discovery Host must be set on DownstreamHostAndPorts if you are not using ReRoute.Host or Ocelot cannot find your service!"))
.BDDfy();
}
private void GivenAConfiguration(FileConfiguration fileConfiguration) private void GivenAConfiguration(FileConfiguration fileConfiguration)
{ {

View File

@ -55,7 +55,13 @@ namespace Ocelot.UnitTests.Configuration
{ {
new FileReRoute new FileReRoute
{ {
DownstreamHost = "test" DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "test"
}
},
} }
} }
}; };

View File

@ -0,0 +1,122 @@
using System.Collections.Generic;
using Ocelot.Configuration;
using Ocelot.Configuration.Builder;
using Ocelot.Configuration.Creator;
using Ocelot.Configuration.File;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.UnitTests.Configuration
{
public class DownstreamAddressesCreatorTests
{
public DownstreamAddressesCreator _creator;
private FileReRoute _reRoute;
private List<DownstreamHostAndPort> _result;
public DownstreamAddressesCreatorTests()
{
_creator = new DownstreamAddressesCreator();
}
[Fact]
public void should_do_nothing()
{
var reRoute = new FileReRoute
{
};
var expected = new List<DownstreamHostAndPort>
{
};
this.Given(x => GivenTheFollowingReRoute(reRoute))
.When(x => WhenICreate())
.Then(x => TheThenFollowingIsReturned(expected))
.BDDfy();
}
[Fact]
public void should_create_downstream_addresses_from_old_downstream_path_and_port()
{
var reRoute = new FileReRoute
{
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "test",
Port = 80
}
},
};
var expected = new List<DownstreamHostAndPort>
{
new DownstreamHostAndPort("test", 80),
};
this.Given(x => GivenTheFollowingReRoute(reRoute))
.When(x => WhenICreate())
.Then(x => TheThenFollowingIsReturned(expected))
.BDDfy();
}
[Fact]
public void should_create_downstream_addresses_from_downstream_host_and_ports()
{
var reRoute = new FileReRoute
{
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "test",
Port = 80
},
new FileHostAndPort
{
Host = "west",
Port = 443
}
}
};
var expected = new List<DownstreamHostAndPort>
{
new DownstreamHostAndPort("test", 80),
new DownstreamHostAndPort("west", 443)
};
this.Given(x => GivenTheFollowingReRoute(reRoute))
.When(x => WhenICreate())
.Then(x => TheThenFollowingIsReturned(expected))
.BDDfy();
}
private void GivenTheFollowingReRoute(FileReRoute reRoute)
{
_reRoute = reRoute;
}
private void WhenICreate()
{
_result = _creator.Create(_reRoute);
}
private void TheThenFollowingIsReturned(List<DownstreamHostAndPort> expecteds)
{
_result.Count.ShouldBe(expecteds.Count);
for (int i = 0; i < _result.Count; i++)
{
var result = _result[i];
var expected = expecteds[i];
result.Host.ShouldBe(expected.Host);
result.Port.ShouldBe(expected.Port);
}
}
}
}

View File

@ -15,6 +15,7 @@ using Xunit;
namespace Ocelot.UnitTests.Configuration namespace Ocelot.UnitTests.Configuration
{ {
using System;
using Ocelot.DependencyInjection; using Ocelot.DependencyInjection;
using Ocelot.Errors; using Ocelot.Errors;
using Ocelot.UnitTests.TestData; using Ocelot.UnitTests.TestData;
@ -40,6 +41,7 @@ namespace Ocelot.UnitTests.Configuration
private Mock<IHttpHandlerOptionsCreator> _httpHandlerOptionsCreator; private Mock<IHttpHandlerOptionsCreator> _httpHandlerOptionsCreator;
private Mock<IAdministrationPath> _adminPath; private Mock<IAdministrationPath> _adminPath;
private readonly Mock<IHeaderFindAndReplaceCreator> _headerFindAndReplaceCreator; private readonly Mock<IHeaderFindAndReplaceCreator> _headerFindAndReplaceCreator;
private readonly Mock<IDownstreamAddressesCreator> _downstreamAddressesCreator;
public FileConfigurationCreatorTests() public FileConfigurationCreatorTests()
{ {
@ -58,6 +60,7 @@ namespace Ocelot.UnitTests.Configuration
_httpHandlerOptionsCreator = new Mock<IHttpHandlerOptionsCreator>(); _httpHandlerOptionsCreator = new Mock<IHttpHandlerOptionsCreator>();
_adminPath = new Mock<IAdministrationPath>(); _adminPath = new Mock<IAdministrationPath>();
_headerFindAndReplaceCreator = new Mock<IHeaderFindAndReplaceCreator>(); _headerFindAndReplaceCreator = new Mock<IHeaderFindAndReplaceCreator>();
_downstreamAddressesCreator = new Mock<IDownstreamAddressesCreator>();
_ocelotConfigurationCreator = new FileOcelotConfigurationCreator( _ocelotConfigurationCreator = new FileOcelotConfigurationCreator(
_fileConfig.Object, _fileConfig.Object,
@ -74,7 +77,8 @@ namespace Ocelot.UnitTests.Configuration
_regionCreator.Object, _regionCreator.Object,
_httpHandlerOptionsCreator.Object, _httpHandlerOptionsCreator.Object,
_adminPath.Object, _adminPath.Object,
_headerFindAndReplaceCreator.Object); _headerFindAndReplaceCreator.Object,
_downstreamAddressesCreator.Object);
} }
[Fact] [Fact]
@ -94,6 +98,7 @@ namespace Ocelot.UnitTests.Configuration
} }
})) }))
.And(x => x.GivenTheFollowingIsReturned(serviceProviderConfig)) .And(x => x.GivenTheFollowingIsReturned(serviceProviderConfig))
.And(x => GivenTheDownstreamAddresses())
.And(x => GivenTheHeaderFindAndReplaceCreatorReturns()) .And(x => GivenTheHeaderFindAndReplaceCreatorReturns())
.And(x => x.GivenTheConfigIsValid()) .And(x => x.GivenTheConfigIsValid())
.When(x => x.WhenICreateTheConfig()) .When(x => x.WhenICreateTheConfig())
@ -113,7 +118,13 @@ namespace Ocelot.UnitTests.Configuration
{ {
new FileReRoute new FileReRoute
{ {
DownstreamHost = "127.0.0.1", DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "127.0.0.1",
}
},
UpstreamPathTemplate = "/api/products/{productId}", UpstreamPathTemplate = "/api/products/{productId}",
DownstreamPathTemplate = "/products/{productId}", DownstreamPathTemplate = "/products/{productId}",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
@ -125,6 +136,7 @@ namespace Ocelot.UnitTests.Configuration
}, },
})) }))
.And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
.And(x => GivenTheDownstreamAddresses())
.And(x => GivenTheHeaderFindAndReplaceCreatorReturns()) .And(x => GivenTheHeaderFindAndReplaceCreatorReturns())
.And(x => x.GivenTheConfigIsValid()) .And(x => x.GivenTheConfigIsValid())
.And(x => x.GivenTheFollowingRegionIsReturned("region")) .And(x => x.GivenTheFollowingRegionIsReturned("region"))
@ -146,7 +158,13 @@ namespace Ocelot.UnitTests.Configuration
{ {
new FileReRoute new FileReRoute
{ {
DownstreamHost = "127.0.0.1", DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "127.0.0.1",
}
},
UpstreamPathTemplate = "/api/products/{productId}", UpstreamPathTemplate = "/api/products/{productId}",
DownstreamPathTemplate = "/products/{productId}", DownstreamPathTemplate = "/products/{productId}",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
@ -154,6 +172,7 @@ namespace Ocelot.UnitTests.Configuration
}, },
})) }))
.And(x => x.GivenTheConfigIsValid()) .And(x => x.GivenTheConfigIsValid())
.And(x => GivenTheDownstreamAddresses())
.And(x => GivenTheHeaderFindAndReplaceCreatorReturns()) .And(x => GivenTheHeaderFindAndReplaceCreatorReturns())
.And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
.When(x => x.WhenICreateTheConfig()) .When(x => x.WhenICreateTheConfig())
@ -180,7 +199,13 @@ namespace Ocelot.UnitTests.Configuration
{ {
new FileReRoute new FileReRoute
{ {
DownstreamHost = "127.0.0.1", DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "127.0.0.1",
}
},
UpstreamPathTemplate = "/api/products/{productId}", UpstreamPathTemplate = "/api/products/{productId}",
DownstreamPathTemplate = "/products/{productId}", DownstreamPathTemplate = "/products/{productId}",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
@ -194,6 +219,7 @@ namespace Ocelot.UnitTests.Configuration
}, },
})) }))
.And(x => x.GivenTheConfigIsValid()) .And(x => x.GivenTheConfigIsValid())
.And(x => GivenTheDownstreamAddresses())
.And(x => GivenTheHeaderFindAndReplaceCreatorReturns()) .And(x => GivenTheHeaderFindAndReplaceCreatorReturns())
.And(x => x.GivenTheFollowingOptionsAreReturned(serviceOptions)) .And(x => x.GivenTheFollowingOptionsAreReturned(serviceOptions))
.And(x => x.GivenTheQosOptionsCreatorReturns(expected)) .And(x => x.GivenTheQosOptionsCreatorReturns(expected))
@ -214,7 +240,13 @@ namespace Ocelot.UnitTests.Configuration
{ {
new FileReRoute new FileReRoute
{ {
DownstreamHost = "127.0.0.1", DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "127.0.0.1",
}
},
UpstreamPathTemplate = "/api/products/{productId}", UpstreamPathTemplate = "/api/products/{productId}",
DownstreamPathTemplate = "/products/{productId}", DownstreamPathTemplate = "/products/{productId}",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
@ -222,13 +254,14 @@ namespace Ocelot.UnitTests.Configuration
}, },
})) }))
.And(x => x.GivenTheConfigIsValid()) .And(x => x.GivenTheConfigIsValid())
.And(x => GivenTheDownstreamAddresses())
.And(x => GivenTheHeaderFindAndReplaceCreatorReturns()) .And(x => GivenTheHeaderFindAndReplaceCreatorReturns())
.And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
.When(x => x.WhenICreateTheConfig()) .When(x => x.WhenICreateTheConfig())
.Then(x => x.ThenTheReRoutesAre(new List<ReRoute> .Then(x => x.ThenTheReRoutesAre(new List<ReRoute>
{ {
new ReRouteBuilder() new ReRouteBuilder()
.WithDownstreamHost("127.0.0.1") .WithDownstreamAddresses(new List<DownstreamHostAndPort>(){new DownstreamHostAndPort("127.0.0.1", 80) })
.WithDownstreamPathTemplate("/products/{productId}") .WithDownstreamPathTemplate("/products/{productId}")
.WithUpstreamPathTemplate("/api/products/{productId}") .WithUpstreamPathTemplate("/api/products/{productId}")
.WithUpstreamHttpMethod(new List<string> { "Get" }) .WithUpstreamHttpMethod(new List<string> { "Get" })
@ -257,6 +290,7 @@ namespace Ocelot.UnitTests.Configuration
}, },
})) }))
.And(x => x.GivenTheConfigIsValid()) .And(x => x.GivenTheConfigIsValid())
.And(x => GivenTheDownstreamAddresses())
.And(x => GivenTheHeaderFindAndReplaceCreatorReturns()) .And(x => GivenTheHeaderFindAndReplaceCreatorReturns())
.And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
.When(x => x.WhenICreateTheConfig()) .When(x => x.WhenICreateTheConfig())
@ -300,6 +334,7 @@ namespace Ocelot.UnitTests.Configuration
} }
})) }))
.And(x => x.GivenTheConfigIsValid()) .And(x => x.GivenTheConfigIsValid())
.And(x => GivenTheDownstreamAddresses())
.And(x => GivenTheHeaderFindAndReplaceCreatorReturns()) .And(x => GivenTheHeaderFindAndReplaceCreatorReturns())
.And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
.When(x => x.WhenICreateTheConfig()) .When(x => x.WhenICreateTheConfig())
@ -336,6 +371,7 @@ namespace Ocelot.UnitTests.Configuration
} }
})) }))
.And(x => x.GivenTheConfigIsValid()) .And(x => x.GivenTheConfigIsValid())
.And(x => GivenTheDownstreamAddresses())
.And(x => GivenTheHeaderFindAndReplaceCreatorReturns()) .And(x => GivenTheHeaderFindAndReplaceCreatorReturns())
.And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
.When(x => x.WhenICreateTheConfig()) .When(x => x.WhenICreateTheConfig())
@ -371,6 +407,7 @@ namespace Ocelot.UnitTests.Configuration
} }
})) }))
.And(x => x.GivenTheConfigIsValid()) .And(x => x.GivenTheConfigIsValid())
.And(x => GivenTheDownstreamAddresses())
.And(x => GivenTheHeaderFindAndReplaceCreatorReturns()) .And(x => GivenTheHeaderFindAndReplaceCreatorReturns())
.And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
.And(x => x.GivenTheUpstreamTemplatePatternCreatorReturns("(?i)/api/products/.*/$")) .And(x => x.GivenTheUpstreamTemplatePatternCreatorReturns("(?i)/api/products/.*/$"))
@ -411,6 +448,7 @@ namespace Ocelot.UnitTests.Configuration
} }
})) }))
.And(x => x.GivenTheConfigIsValid()) .And(x => x.GivenTheConfigIsValid())
.And(x => GivenTheDownstreamAddresses())
.And(x => GivenTheHeaderFindAndReplaceCreatorReturns()) .And(x => GivenTheHeaderFindAndReplaceCreatorReturns())
.And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
.And(x => x.GivenTheRequestIdCreatorReturns("blahhhh")) .And(x => x.GivenTheRequestIdCreatorReturns("blahhhh"))
@ -441,7 +479,13 @@ namespace Ocelot.UnitTests.Configuration
{ {
new FileReRoute new FileReRoute
{ {
DownstreamHost = "127.0.0.1", DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "127.0.0.1",
}
},
UpstreamPathTemplate = "/api/products/{productId}", UpstreamPathTemplate = "/api/products/{productId}",
DownstreamPathTemplate = "/products/{productId}", DownstreamPathTemplate = "/products/{productId}",
UpstreamHttpMethod = new List<string> { "Get" } UpstreamHttpMethod = new List<string> { "Get" }
@ -449,6 +493,7 @@ namespace Ocelot.UnitTests.Configuration
}, },
})) }))
.And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
.And(x => GivenTheDownstreamAddresses())
.And(x => GivenTheHeaderFindAndReplaceCreatorReturns()) .And(x => GivenTheHeaderFindAndReplaceCreatorReturns())
.And(x => x.GivenTheConfigIsValid()) .And(x => x.GivenTheConfigIsValid())
.And(x => x.GivenTheFollowingHttpHandlerOptionsAreReturned(httpHandlerOptions)) .And(x => x.GivenTheFollowingHttpHandlerOptionsAreReturned(httpHandlerOptions))
@ -484,6 +529,7 @@ namespace Ocelot.UnitTests.Configuration
}; };
this.Given(x => x.GivenTheConfigIs(fileConfig)) this.Given(x => x.GivenTheConfigIs(fileConfig))
.And(x => GivenTheDownstreamAddresses())
.And(x => x.GivenTheConfigIsValid()) .And(x => x.GivenTheConfigIsValid())
.And(x => GivenTheHeaderFindAndReplaceCreatorReturns()) .And(x => GivenTheHeaderFindAndReplaceCreatorReturns())
.And(x => x.GivenTheAuthOptionsCreatorReturns(authenticationOptions)) .And(x => x.GivenTheAuthOptionsCreatorReturns(authenticationOptions))
@ -519,6 +565,7 @@ namespace Ocelot.UnitTests.Configuration
}; };
this.Given(x => x.GivenTheConfigIs(fileConfig)) this.Given(x => x.GivenTheConfigIs(fileConfig))
.And(x => GivenTheDownstreamAddresses())
.And(x => x.GivenTheConfigIsValid()) .And(x => x.GivenTheConfigIsValid())
.And(x => GivenTheHeaderFindAndReplaceCreatorReturns()) .And(x => GivenTheHeaderFindAndReplaceCreatorReturns())
.And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
@ -536,6 +583,7 @@ namespace Ocelot.UnitTests.Configuration
var errors = new List<Error> {new FileValidationFailedError("some message")}; var errors = new List<Error> {new FileValidationFailedError("some message")};
this.Given(x => x.GivenTheConfigIs(new FileConfiguration())) this.Given(x => x.GivenTheConfigIs(new FileConfiguration()))
.And(x => GivenTheDownstreamAddresses())
.And(x => x.GivenTheConfigIsInvalid(errors)) .And(x => x.GivenTheConfigIsInvalid(errors))
.When(x => x.WhenICreateTheConfig()) .When(x => x.WhenICreateTheConfig())
.Then(x => x.ThenTheErrorsAreReturned(errors)) .Then(x => x.ThenTheErrorsAreReturned(errors))
@ -719,5 +767,10 @@ namespace Ocelot.UnitTests.Configuration
{ {
_httpHandlerOptionsCreator.Verify(x => x.Create(_fileConfiguration.ReRoutes[0]), Times.Once()); _httpHandlerOptionsCreator.Verify(x => x.Create(_fileConfiguration.ReRoutes[0]), Times.Once());
} }
private void GivenTheDownstreamAddresses()
{
_downstreamAddressesCreator.Setup(x => x.Create(It.IsAny<FileReRoute>())).Returns(new List<DownstreamHostAndPort>());
}
} }
} }

View File

@ -89,18 +89,25 @@ namespace Ocelot.UnitTests.Configuration
_result = _repo.Get().Result.Data; _result = _repo.Get().Result.Data;
} }
private void ThenTheConfigurationIsStoredAs(FileConfiguration expected) private void ThenTheConfigurationIsStoredAs(FileConfiguration expecteds)
{ {
_result.GlobalConfiguration.RequestIdKey.ShouldBe(expected.GlobalConfiguration.RequestIdKey); _result.GlobalConfiguration.RequestIdKey.ShouldBe(expecteds.GlobalConfiguration.RequestIdKey);
_result.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Host); _result.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expecteds.GlobalConfiguration.ServiceDiscoveryProvider.Host);
_result.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Port); _result.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(expecteds.GlobalConfiguration.ServiceDiscoveryProvider.Port);
for(var i = 0; i < _result.ReRoutes.Count; i++) for(var i = 0; i < _result.ReRoutes.Count; i++)
{ {
_result.ReRoutes[i].DownstreamHost.ShouldBe(expected.ReRoutes[i].DownstreamHost); for (int j = 0; j < _result.ReRoutes[i].DownstreamHostAndPorts.Count; j++)
_result.ReRoutes[i].DownstreamPathTemplate.ShouldBe(expected.ReRoutes[i].DownstreamPathTemplate); {
_result.ReRoutes[i].DownstreamPort.ShouldBe(expected.ReRoutes[i].DownstreamPort); var result = _result.ReRoutes[i].DownstreamHostAndPorts[j];
_result.ReRoutes[i].DownstreamScheme.ShouldBe(expected.ReRoutes[i].DownstreamScheme); var expected = expecteds.ReRoutes[i].DownstreamHostAndPorts[j];
result.Host.ShouldBe(expected.Host);
result.Port.ShouldBe(expected.Port);
}
_result.ReRoutes[i].DownstreamPathTemplate.ShouldBe(expecteds.ReRoutes[i].DownstreamPathTemplate);
_result.ReRoutes[i].DownstreamScheme.ShouldBe(expecteds.ReRoutes[i].DownstreamScheme);
} }
} }
@ -123,18 +130,25 @@ namespace Ocelot.UnitTests.Configuration
_result = _repo.Get().Result.Data; _result = _repo.Get().Result.Data;
} }
private void ThenTheFollowingIsReturned(FileConfiguration expected) private void ThenTheFollowingIsReturned(FileConfiguration expecteds)
{ {
_result.GlobalConfiguration.RequestIdKey.ShouldBe(expected.GlobalConfiguration.RequestIdKey); _result.GlobalConfiguration.RequestIdKey.ShouldBe(expecteds.GlobalConfiguration.RequestIdKey);
_result.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Host); _result.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expecteds.GlobalConfiguration.ServiceDiscoveryProvider.Host);
_result.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Port); _result.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(expecteds.GlobalConfiguration.ServiceDiscoveryProvider.Port);
for(var i = 0; i < _result.ReRoutes.Count; i++) for(var i = 0; i < _result.ReRoutes.Count; i++)
{ {
_result.ReRoutes[i].DownstreamHost.ShouldBe(expected.ReRoutes[i].DownstreamHost); for (int j = 0; j < _result.ReRoutes[i].DownstreamHostAndPorts.Count; j++)
_result.ReRoutes[i].DownstreamPathTemplate.ShouldBe(expected.ReRoutes[i].DownstreamPathTemplate); {
_result.ReRoutes[i].DownstreamPort.ShouldBe(expected.ReRoutes[i].DownstreamPort); var result = _result.ReRoutes[i].DownstreamHostAndPorts[j];
_result.ReRoutes[i].DownstreamScheme.ShouldBe(expected.ReRoutes[i].DownstreamScheme); var expected = expecteds.ReRoutes[i].DownstreamHostAndPorts[j];
result.Host.ShouldBe(expected.Host);
result.Port.ShouldBe(expected.Port);
}
_result.ReRoutes[i].DownstreamPathTemplate.ShouldBe(expecteds.ReRoutes[i].DownstreamPathTemplate);
_result.ReRoutes[i].DownstreamScheme.ShouldBe(expecteds.ReRoutes[i].DownstreamScheme);
} }
} }
@ -144,8 +158,14 @@ namespace Ocelot.UnitTests.Configuration
{ {
new FileReRoute new FileReRoute
{ {
DownstreamHost = "123.12.12.12", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 80, {
new FileHostAndPort
{
Host = "123.12.12.12",
Port = 80,
}
},
DownstreamScheme = "https", DownstreamScheme = "https",
DownstreamPathTemplate = "/asdfs/test/{test}" DownstreamPathTemplate = "/asdfs/test/{test}"
} }
@ -173,8 +193,14 @@ namespace Ocelot.UnitTests.Configuration
{ {
new FileReRoute new FileReRoute
{ {
DownstreamHost = "localhost", DownstreamHostAndPorts = new List<FileHostAndPort>
DownstreamPort = 80, {
new FileHostAndPort
{
Host = "localhost",
Port = 80,
}
},
DownstreamScheme = "https", DownstreamScheme = "https",
DownstreamPathTemplate = "/test/test/{test}" DownstreamPathTemplate = "/test/test/{test}"
} }

View File

@ -106,7 +106,7 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator
private void WhenIBuildTheUrl() private void WhenIBuildTheUrl()
{ {
_result = _urlBuilder.Build(_dsPath, _dsScheme, new HostAndPort(_dsHost, _dsPort)); _result = _urlBuilder.Build(_dsPath, _dsScheme, new ServiceHostAndPort(_dsHost, _dsPort));
} }
private void ThenTheUrlIsReturned(string expected) private void ThenTheUrlIsReturned(string expected)

View File

@ -37,6 +37,7 @@ namespace Ocelot.UnitTests.Headers
public void should_call_pre_and_post_header_transforms() public void should_call_pre_and_post_header_transforms()
{ {
this.Given(x => GivenTheFollowingRequest()) this.Given(x => GivenTheFollowingRequest())
.And(x => GivenTheDownstreamRequestIs())
.And(x => GivenTheReRouteHasPreFindAndReplaceSetUp()) .And(x => GivenTheReRouteHasPreFindAndReplaceSetUp())
.And(x => GivenTheHttpResponseMessageIs()) .And(x => GivenTheHttpResponseMessageIs())
.When(x => WhenICallTheMiddleware()) .When(x => WhenICallTheMiddleware())
@ -45,6 +46,13 @@ namespace Ocelot.UnitTests.Headers
.BDDfy(); .BDDfy();
} }
private void GivenTheDownstreamRequestIs()
{
var request = new HttpRequestMessage();
var response = new OkResponse<HttpRequestMessage>(request);
ScopedRepository.Setup(x => x.Get<HttpRequestMessage>("DownstreamRequest")).Returns(response);
}
private void GivenTheHttpResponseMessageIs() private void GivenTheHttpResponseMessageIs()
{ {
var httpResponseMessage = new HttpResponseMessage(); var httpResponseMessage = new HttpResponseMessage();
@ -68,7 +76,7 @@ namespace Ocelot.UnitTests.Headers
private void ThenTheIHttpResponseHeaderReplacerIsCalledCorrectly() private void ThenTheIHttpResponseHeaderReplacerIsCalledCorrectly()
{ {
_postReplacer.Verify(x => x.Replace(It.IsAny<HttpResponseMessage>(), It.IsAny<List<HeaderFindAndReplace>>()), Times.Once); _postReplacer.Verify(x => x.Replace(It.IsAny<HttpResponseMessage>(), It.IsAny<List<HeaderFindAndReplace>>(), It.IsAny<HttpRequestMessage>()), Times.Once);
} }
private void GivenTheFollowingRequest() private void GivenTheFollowingRequest()

View File

@ -16,6 +16,7 @@ namespace Ocelot.UnitTests.Headers
private HttpResponseHeaderReplacer _replacer; private HttpResponseHeaderReplacer _replacer;
private List<HeaderFindAndReplace> _headerFindAndReplaces; private List<HeaderFindAndReplace> _headerFindAndReplaces;
private Response _result; private Response _result;
private HttpRequestMessage _request;
public HttpResponseHeaderReplacerTests() public HttpResponseHeaderReplacerTests()
{ {
@ -52,6 +53,143 @@ namespace Ocelot.UnitTests.Headers
.BDDfy(); .BDDfy();
} }
[Fact]
public void should_replace_downstream_base_url_with_ocelot_base_url()
{
var downstreamUrl = "http://downstream.com/";
var request = new HttpRequestMessage();
request.RequestUri = new System.Uri(downstreamUrl);
var response = new HttpResponseMessage();
response.Headers.Add("Location", downstreamUrl);
var fAndRs = new List<HeaderFindAndReplace>();
fAndRs.Add(new HeaderFindAndReplace("Location", "{DownstreamBaseUrl}", "http://ocelot.com/", 0));
this.Given(x => GivenTheHttpResponse(response))
.And(x => GivenTheRequestIs(request))
.And(x => GivenTheFollowingHeaderReplacements(fAndRs))
.When(x => WhenICallTheReplacer())
.Then(x => ThenTheHeaderShouldBe("Location", "http://ocelot.com/"))
.BDDfy();
}
[Fact]
public void should_replace_downstream_base_url_with_ocelot_base_url_with_port()
{
var downstreamUrl = "http://downstream.com/";
var request = new HttpRequestMessage();
request.RequestUri = new System.Uri(downstreamUrl);
var response = new HttpResponseMessage();
response.Headers.Add("Location", downstreamUrl);
var fAndRs = new List<HeaderFindAndReplace>();
fAndRs.Add(new HeaderFindAndReplace("Location", "{DownstreamBaseUrl}", "http://ocelot.com:123/", 0));
this.Given(x => GivenTheHttpResponse(response))
.And(x => GivenTheRequestIs(request))
.And(x => GivenTheFollowingHeaderReplacements(fAndRs))
.When(x => WhenICallTheReplacer())
.Then(x => ThenTheHeaderShouldBe("Location", "http://ocelot.com:123/"))
.BDDfy();
}
[Fact]
public void should_replace_downstream_base_url_with_ocelot_base_url_and_path()
{
var downstreamUrl = "http://downstream.com/test/product";
var request = new HttpRequestMessage();
request.RequestUri = new System.Uri(downstreamUrl);
var response = new HttpResponseMessage();
response.Headers.Add("Location", downstreamUrl);
var fAndRs = new List<HeaderFindAndReplace>();
fAndRs.Add(new HeaderFindAndReplace("Location", "{DownstreamBaseUrl}", "http://ocelot.com/", 0));
this.Given(x => GivenTheHttpResponse(response))
.And(x => GivenTheRequestIs(request))
.And(x => GivenTheFollowingHeaderReplacements(fAndRs))
.When(x => WhenICallTheReplacer())
.Then(x => ThenTheHeaderShouldBe("Location", "http://ocelot.com/test/product"))
.BDDfy();
}
[Fact]
public void should_replace_downstream_base_url_with_ocelot_base_url_with_path_and_port()
{
var downstreamUrl = "http://downstream.com/test/product";
var request = new HttpRequestMessage();
request.RequestUri = new System.Uri(downstreamUrl);
var response = new HttpResponseMessage();
response.Headers.Add("Location", downstreamUrl);
var fAndRs = new List<HeaderFindAndReplace>();
fAndRs.Add(new HeaderFindAndReplace("Location", "{DownstreamBaseUrl}", "http://ocelot.com:123/", 0));
this.Given(x => GivenTheHttpResponse(response))
.And(x => GivenTheRequestIs(request))
.And(x => GivenTheFollowingHeaderReplacements(fAndRs))
.When(x => WhenICallTheReplacer())
.Then(x => ThenTheHeaderShouldBe("Location", "http://ocelot.com:123/test/product"))
.BDDfy();
}
[Fact]
public void should_replace_downstream_base_url_and_port_with_ocelot_base_url()
{
var downstreamUrl = "http://downstream.com:123/test/product";
var request = new HttpRequestMessage();
request.RequestUri = new System.Uri(downstreamUrl);
var response = new HttpResponseMessage();
response.Headers.Add("Location", downstreamUrl);
var fAndRs = new List<HeaderFindAndReplace>();
fAndRs.Add(new HeaderFindAndReplace("Location", "{DownstreamBaseUrl}", "http://ocelot.com/", 0));
this.Given(x => GivenTheHttpResponse(response))
.And(x => GivenTheRequestIs(request))
.And(x => GivenTheFollowingHeaderReplacements(fAndRs))
.When(x => WhenICallTheReplacer())
.Then(x => ThenTheHeaderShouldBe("Location", "http://ocelot.com/test/product"))
.BDDfy();
}
[Fact]
public void should_replace_downstream_base_url_and_port_with_ocelot_base_url_and_port()
{
var downstreamUrl = "http://downstream.com:123/test/product";
var request = new HttpRequestMessage();
request.RequestUri = new System.Uri(downstreamUrl);
var response = new HttpResponseMessage();
response.Headers.Add("Location", downstreamUrl);
var fAndRs = new List<HeaderFindAndReplace>();
fAndRs.Add(new HeaderFindAndReplace("Location", "{DownstreamBaseUrl}", "http://ocelot.com:321/", 0));
this.Given(x => GivenTheHttpResponse(response))
.And(x => GivenTheRequestIs(request))
.And(x => GivenTheFollowingHeaderReplacements(fAndRs))
.When(x => WhenICallTheReplacer())
.Then(x => ThenTheHeaderShouldBe("Location", "http://ocelot.com:321/test/product"))
.BDDfy();
}
private void GivenTheRequestIs(HttpRequestMessage request)
{
_request = request;
}
private void ThenTheHeadersAreNotReplaced() private void ThenTheHeadersAreNotReplaced()
{ {
@ -75,7 +213,13 @@ namespace Ocelot.UnitTests.Headers
private void WhenICallTheReplacer() private void WhenICallTheReplacer()
{ {
_result = _replacer.Replace(_response, _headerFindAndReplaces); _result = _replacer.Replace(_response, _headerFindAndReplaces, _request);
}
private void ThenTheHeaderShouldBe(string key, string value)
{
var test = _response.Headers.GetValues(key);
test.First().ShouldBe(value);
} }
private void ThenTheHeadersAreReplaced() private void ThenTheHeadersAreReplaced()

View File

@ -12,8 +12,8 @@ namespace Ocelot.UnitTests.LoadBalancer
{ {
public class LeastConnectionTests public class LeastConnectionTests
{ {
private HostAndPort _hostAndPort; private ServiceHostAndPort _hostAndPort;
private Response<HostAndPort> _result; private Response<ServiceHostAndPort> _result;
private LeastConnection _leastConnection; private LeastConnection _leastConnection;
private List<Service> _services; private List<Service> _services;
private Random _random; private Random _random;
@ -30,8 +30,8 @@ namespace Ocelot.UnitTests.LoadBalancer
var availableServices = new List<Service> var availableServices = new List<Service>
{ {
new Service(serviceName, new HostAndPort("127.0.0.1", 80), string.Empty, string.Empty, new string[0]), new Service(serviceName, new ServiceHostAndPort("127.0.0.1", 80), string.Empty, string.Empty, new string[0]),
new Service(serviceName, new HostAndPort("127.0.0.2", 80), string.Empty, string.Empty, new string[0]), new Service(serviceName, new ServiceHostAndPort("127.0.0.2", 80), string.Empty, string.Empty, new string[0]),
}; };
_services = availableServices; _services = availableServices;
@ -54,8 +54,8 @@ namespace Ocelot.UnitTests.LoadBalancer
var availableServices = new List<Service> var availableServices = new List<Service>
{ {
new Service(serviceName, new HostAndPort("127.0.0.1", 80), string.Empty, string.Empty, new string[0]), new Service(serviceName, new ServiceHostAndPort("127.0.0.1", 80), string.Empty, string.Empty, new string[0]),
new Service(serviceName, new HostAndPort("127.0.0.2", 80), string.Empty, string.Empty, new string[0]), new Service(serviceName, new ServiceHostAndPort("127.0.0.2", 80), string.Empty, string.Empty, new string[0]),
}; };
_leastConnection = new LeastConnection(() => Task.FromResult(availableServices), serviceName); _leastConnection = new LeastConnection(() => Task.FromResult(availableServices), serviceName);
@ -69,7 +69,7 @@ namespace Ocelot.UnitTests.LoadBalancer
availableServices = new List<Service> availableServices = new List<Service>
{ {
new Service(serviceName, new HostAndPort("127.0.0.1", 80), string.Empty, string.Empty, new string[0]), new Service(serviceName, new ServiceHostAndPort("127.0.0.1", 80), string.Empty, string.Empty, new string[0]),
}; };
hostAndPortOne = _leastConnection.Lease().Result; hostAndPortOne = _leastConnection.Lease().Result;
@ -81,8 +81,8 @@ namespace Ocelot.UnitTests.LoadBalancer
availableServices = new List<Service> availableServices = new List<Service>
{ {
new Service(serviceName, new HostAndPort("127.0.0.1", 80), string.Empty, string.Empty, new string[0]), new Service(serviceName, new ServiceHostAndPort("127.0.0.1", 80), string.Empty, string.Empty, new string[0]),
new Service(serviceName, new HostAndPort("127.0.0.2", 80), string.Empty, string.Empty, new string[0]), new Service(serviceName, new ServiceHostAndPort("127.0.0.2", 80), string.Empty, string.Empty, new string[0]),
}; };
hostAndPortOne = _leastConnection.Lease().Result; hostAndPortOne = _leastConnection.Lease().Result;
@ -106,7 +106,7 @@ namespace Ocelot.UnitTests.LoadBalancer
{ {
var serviceName = "products"; var serviceName = "products";
var hostAndPort = new HostAndPort("localhost", 80); var hostAndPort = new ServiceHostAndPort("localhost", 80);
var availableServices = new List<Service> var availableServices = new List<Service>
{ {
@ -127,9 +127,9 @@ namespace Ocelot.UnitTests.LoadBalancer
var availableServices = new List<Service> var availableServices = new List<Service>
{ {
new Service(serviceName, new HostAndPort("127.0.0.1", 80), string.Empty, string.Empty, new string[0]), new Service(serviceName, new ServiceHostAndPort("127.0.0.1", 80), string.Empty, string.Empty, new string[0]),
new Service(serviceName, new HostAndPort("127.0.0.2", 80), string.Empty, string.Empty, new string[0]), new Service(serviceName, new ServiceHostAndPort("127.0.0.2", 80), string.Empty, string.Empty, new string[0]),
new Service(serviceName, new HostAndPort("127.0.0.3", 80), string.Empty, string.Empty, new string[0]) new Service(serviceName, new ServiceHostAndPort("127.0.0.3", 80), string.Empty, string.Empty, new string[0])
}; };
_services = availableServices; _services = availableServices;
@ -155,8 +155,8 @@ namespace Ocelot.UnitTests.LoadBalancer
var availableServices = new List<Service> var availableServices = new List<Service>
{ {
new Service(serviceName, new HostAndPort("127.0.0.1", 80), string.Empty, string.Empty, new string[0]), new Service(serviceName, new ServiceHostAndPort("127.0.0.1", 80), string.Empty, string.Empty, new string[0]),
new Service(serviceName, new HostAndPort("127.0.0.2", 80), string.Empty, string.Empty, new string[0]), new Service(serviceName, new ServiceHostAndPort("127.0.0.2", 80), string.Empty, string.Empty, new string[0]),
}; };
_services = availableServices; _services = availableServices;
@ -186,8 +186,8 @@ namespace Ocelot.UnitTests.LoadBalancer
var availableServices = new List<Service> var availableServices = new List<Service>
{ {
new Service(serviceName, new HostAndPort("127.0.0.1", 80), string.Empty, string.Empty, new string[0]), new Service(serviceName, new ServiceHostAndPort("127.0.0.1", 80), string.Empty, string.Empty, new string[0]),
new Service(serviceName, new HostAndPort("127.0.0.2", 80), string.Empty, string.Empty, new string[0]), new Service(serviceName, new ServiceHostAndPort("127.0.0.2", 80), string.Empty, string.Empty, new string[0]),
}; };
_services = availableServices; _services = availableServices;
@ -222,7 +222,7 @@ namespace Ocelot.UnitTests.LoadBalancer
{ {
var serviceName = "products"; var serviceName = "products";
var hostAndPort = new HostAndPort("localhost", 80); var hostAndPort = new ServiceHostAndPort("localhost", 80);
this.Given(x => x.GivenAHostAndPort(hostAndPort)) this.Given(x => x.GivenAHostAndPort(hostAndPort))
.And(x => x.GivenTheLoadBalancerStarts(null, serviceName)) .And(x => x.GivenTheLoadBalancerStarts(null, serviceName))
.When(x => x.WhenIGetTheNextHostAndPort()) .When(x => x.WhenIGetTheNextHostAndPort())
@ -235,7 +235,7 @@ namespace Ocelot.UnitTests.LoadBalancer
{ {
var serviceName = "products"; var serviceName = "products";
var hostAndPort = new HostAndPort("localhost", 80); var hostAndPort = new ServiceHostAndPort("localhost", 80);
this.Given(x => x.GivenAHostAndPort(hostAndPort)) this.Given(x => x.GivenAHostAndPort(hostAndPort))
.And(x => x.GivenTheLoadBalancerStarts(new List<Service>(), serviceName)) .And(x => x.GivenTheLoadBalancerStarts(new List<Service>(), serviceName))
.When(x => x.WhenIGetTheNextHostAndPort()) .When(x => x.WhenIGetTheNextHostAndPort())
@ -266,7 +266,7 @@ namespace Ocelot.UnitTests.LoadBalancer
GivenTheLoadBalancerStarts(services, serviceName); GivenTheLoadBalancerStarts(services, serviceName);
} }
private void GivenAHostAndPort(HostAndPort hostAndPort) private void GivenAHostAndPort(ServiceHostAndPort hostAndPort)
{ {
_hostAndPort = hostAndPort; _hostAndPort = hostAndPort;
} }

View File

@ -138,12 +138,12 @@ namespace Ocelot.UnitTests.LoadBalancer
class FakeLoadBalancer : ILoadBalancer class FakeLoadBalancer : ILoadBalancer
{ {
public Task<Response<HostAndPort>> Lease() public Task<Response<ServiceHostAndPort>> Lease()
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public void Release(HostAndPort hostAndPort) public void Release(ServiceHostAndPort hostAndPort)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
@ -151,12 +151,12 @@ namespace Ocelot.UnitTests.LoadBalancer
class FakeRoundRobinLoadBalancer : ILoadBalancer class FakeRoundRobinLoadBalancer : ILoadBalancer
{ {
public Task<Response<HostAndPort>> Lease() public Task<Response<ServiceHostAndPort>> Lease()
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public void Release(HostAndPort hostAndPort) public void Release(ServiceHostAndPort hostAndPort)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }

View File

@ -23,10 +23,10 @@ namespace Ocelot.UnitTests.LoadBalancer
{ {
private readonly Mock<ILoadBalancerHouse> _loadBalancerHouse; private readonly Mock<ILoadBalancerHouse> _loadBalancerHouse;
private readonly Mock<ILoadBalancer> _loadBalancer; private readonly Mock<ILoadBalancer> _loadBalancer;
private HostAndPort _hostAndPort; private ServiceHostAndPort _hostAndPort;
private OkResponse<DownstreamRoute> _downstreamRoute; private OkResponse<DownstreamRoute> _downstreamRoute;
private ErrorResponse<ILoadBalancer> _getLoadBalancerHouseError; private ErrorResponse<ILoadBalancer> _getLoadBalancerHouseError;
private ErrorResponse<HostAndPort> _getHostAndPortError; private ErrorResponse<ServiceHostAndPort> _getHostAndPortError;
private HttpRequestMessage _downstreamRequest; private HttpRequestMessage _downstreamRequest;
private ServiceProviderConfiguration _config; private ServiceProviderConfiguration _config;
@ -133,7 +133,7 @@ namespace Ocelot.UnitTests.LoadBalancer
private void GivenTheLoadBalancerReturnsAnError() private void GivenTheLoadBalancerReturnsAnError()
{ {
_getHostAndPortError = new ErrorResponse<HostAndPort>(new List<Error>() { new ServicesAreNullError($"services were null for bah") }); _getHostAndPortError = new ErrorResponse<ServiceHostAndPort>(new List<Error>() { new ServicesAreNullError($"services were null for bah") });
_loadBalancer _loadBalancer
.Setup(x => x.Lease()) .Setup(x => x.Lease())
.ReturnsAsync(_getHostAndPortError); .ReturnsAsync(_getHostAndPortError);
@ -141,10 +141,10 @@ namespace Ocelot.UnitTests.LoadBalancer
private void GivenTheLoadBalancerReturns() private void GivenTheLoadBalancerReturns()
{ {
_hostAndPort = new HostAndPort("127.0.0.1", 80); _hostAndPort = new ServiceHostAndPort("127.0.0.1", 80);
_loadBalancer _loadBalancer
.Setup(x => x.Lease()) .Setup(x => x.Lease())
.ReturnsAsync(new OkResponse<HostAndPort>(_hostAndPort)); .ReturnsAsync(new OkResponse<ServiceHostAndPort>(_hostAndPort));
} }
private void GivenTheDownStreamRouteIs(DownstreamRoute downstreamRoute) private void GivenTheDownStreamRouteIs(DownstreamRoute downstreamRoute)

View File

@ -12,12 +12,12 @@ namespace Ocelot.UnitTests.LoadBalancer
{ {
private List<Service> _services; private List<Service> _services;
private NoLoadBalancer _loadBalancer; private NoLoadBalancer _loadBalancer;
private Response<HostAndPort> _result; private Response<ServiceHostAndPort> _result;
[Fact] [Fact]
public void should_return_host_and_port() public void should_return_host_and_port()
{ {
var hostAndPort = new HostAndPort("127.0.0.1", 80); var hostAndPort = new ServiceHostAndPort("127.0.0.1", 80);
var services = new List<Service> var services = new List<Service>
{ {
@ -40,7 +40,7 @@ namespace Ocelot.UnitTests.LoadBalancer
_result = _loadBalancer.Lease().Result; _result = _loadBalancer.Lease().Result;
} }
private void ThenTheHostAndPortIs(HostAndPort expected) private void ThenTheHostAndPortIs(ServiceHostAndPort expected)
{ {
_result.Data.ShouldBe(expected); _result.Data.ShouldBe(expected);
} }

View File

@ -14,15 +14,15 @@ namespace Ocelot.UnitTests.LoadBalancer
{ {
private readonly RoundRobin _roundRobin; private readonly RoundRobin _roundRobin;
private readonly List<Service> _services; private readonly List<Service> _services;
private Response<HostAndPort> _hostAndPort; private Response<ServiceHostAndPort> _hostAndPort;
public RoundRobinTests() public RoundRobinTests()
{ {
_services = new List<Service> _services = new List<Service>
{ {
new Service("product", new HostAndPort("127.0.0.1", 5000), string.Empty, string.Empty, new string[0]), new Service("product", new ServiceHostAndPort("127.0.0.1", 5000), string.Empty, string.Empty, new string[0]),
new Service("product", new HostAndPort("127.0.0.1", 5001), string.Empty, string.Empty, new string[0]), new Service("product", new ServiceHostAndPort("127.0.0.1", 5001), string.Empty, string.Empty, new string[0]),
new Service("product", new HostAndPort("127.0.0.1", 5001), string.Empty, string.Empty, new string[0]) new Service("product", new ServiceHostAndPort("127.0.0.1", 5001), string.Empty, string.Empty, new string[0])
}; };
_roundRobin = new RoundRobin(() => Task.FromResult(_services)); _roundRobin = new RoundRobin(() => Task.FromResult(_services));

View File

@ -16,7 +16,7 @@ namespace Ocelot.UnitTests.ServiceDiscovery
[Fact] [Fact]
public void should_return_services() public void should_return_services()
{ {
var hostAndPort = new HostAndPort("127.0.0.1", 80); var hostAndPort = new ServiceHostAndPort("127.0.0.1", 80);
var services = new List<Service> var services = new List<Service>
{ {

View File

@ -1,3 +1,5 @@
using System;
using System.Collections.Generic;
using Ocelot.Configuration; using Ocelot.Configuration;
using Ocelot.Configuration.Builder; using Ocelot.Configuration.Builder;
using Ocelot.ServiceDiscovery; using Ocelot.ServiceDiscovery;
@ -33,6 +35,42 @@ namespace Ocelot.UnitTests.ServiceDiscovery
.BDDfy(); .BDDfy();
} }
[Fact]
public void should_return_list_of_configuration_services()
{
var serviceConfig = new ServiceProviderConfigurationBuilder()
.Build();
var downstreamAddresses = new List<DownstreamHostAndPort>()
{
new DownstreamHostAndPort("asdf.com", 80),
new DownstreamHostAndPort("abc.com", 80)
};
var reRoute = new ReRouteBuilder().WithDownstreamAddresses(downstreamAddresses).Build();
this.Given(x => x.GivenTheReRoute(serviceConfig, reRoute))
.When(x => x.WhenIGetTheServiceProvider())
.Then(x => x.ThenTheServiceProviderIs<ConfigurationServiceProvider>())
.Then(x => ThenTheFollowingServicesAreReturned(downstreamAddresses))
.BDDfy();
}
private void ThenTheFollowingServicesAreReturned(List<DownstreamHostAndPort> downstreamAddresses)
{
var result = (ConfigurationServiceProvider)_result;
var services = result.Get().Result;
for (int i = 0; i < services.Count; i++)
{
var service = services[i];
var downstreamAddress = downstreamAddresses[i];
service.HostAndPort.DownstreamHost.ShouldBe(downstreamAddress.Host);
service.HostAndPort.DownstreamPort.ShouldBe(downstreamAddress.Port);
}
}
[Fact] [Fact]
public void should_return_consul_service_provider() public void should_return_consul_service_provider()
{ {

View File

@ -52,13 +52,13 @@ namespace Ocelot.UnitTests.ServiceDiscovery
private void GivenAServiceIsRegistered(string name, string address, int port) private void GivenAServiceIsRegistered(string name, string address, int port)
{ {
_service = new Service(name, new HostAndPort(address, port), string.Empty, string.Empty, new string[0]); _service = new Service(name, new ServiceHostAndPort(address, port), string.Empty, string.Empty, new string[0]);
_serviceRepository.Set(_service); _serviceRepository.Set(_service);
} }
private void GivenAServiceToRegister(string name, string address, int port) private void GivenAServiceToRegister(string name, string address, int port)
{ {
_service = new Service(name, new HostAndPort(address, port), string.Empty, string.Empty, new string[0]); _service = new Service(name, new ServiceHostAndPort(address, port), string.Empty, string.Empty, new string[0]);
} }
private void WhenIRegisterTheService() private void WhenIRegisterTheService()