mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-04-22 06:22:50 +08:00
commit
cc33d9f86e
14
README.md
14
README.md
@ -9,9 +9,9 @@
|
|||||||
|
|
||||||
# Ocelot
|
# Ocelot
|
||||||
|
|
||||||
Ocelot is a .NET Api Gateway. This project is aimed at people using .NET running
|
Ocelot is a .NET API Gateway. This project is aimed at people using .NET running
|
||||||
a micro services / service orientated architecture
|
a micro services / service orientated architecture
|
||||||
that need a unified point of entry into their system. However it will work with anything that speaks HTTP and run on any platform that asp.net core supports.
|
that need a unified point of entry into their system. However it will work with anything that speaks HTTP and run on any platform that ASP.NET Core supports.
|
||||||
|
|
||||||
In particular I want easy integration with
|
In particular I want easy integration with
|
||||||
IdentityServer reference and bearer tokens.
|
IdentityServer reference and bearer tokens.
|
||||||
@ -61,6 +61,10 @@ Install Ocelot and it's dependencies using NuGet.
|
|||||||
|
|
||||||
`Install-Package Ocelot`
|
`Install-Package Ocelot`
|
||||||
|
|
||||||
|
Or via the .NET Core CLI:
|
||||||
|
|
||||||
|
`dotnet add package ocelot`
|
||||||
|
|
||||||
All versions can be found [here](https://www.nuget.org/packages/Ocelot/)
|
All versions can be found [here](https://www.nuget.org/packages/Ocelot/)
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
@ -83,6 +87,12 @@ advice on the easiest way to do things :)
|
|||||||
|
|
||||||
Finally we mark all existing issues as help wanted, small, medium and large effort. If you want to contriute for the first time I suggest looking at a help wanted & small effort issue :)
|
Finally we mark all existing issues as help wanted, small, medium and large effort. If you want to contriute for the first time I suggest looking at a help wanted & small effort issue :)
|
||||||
|
|
||||||
|
## Donate
|
||||||
|
|
||||||
|
If you think this project is worth supporting financially please make a contribution using the button below!
|
||||||
|
|
||||||
|
[](https://www.paypal.me/ThreeMammals/)
|
||||||
|
|
||||||
## Things that are currently annoying me
|
## Things that are currently annoying me
|
||||||
|
|
||||||
[ Get more details at **codescene.io**.](https://codescene.io/projects/697/jobs/latest-successful/results)
|
[ Get more details at **codescene.io**.](https://codescene.io/projects/697/jobs/latest-successful/results)
|
||||||
|
@ -4,8 +4,8 @@
|
|||||||
#addin nuget:?package=Newtonsoft.Json&version=9.0.1
|
#addin nuget:?package=Newtonsoft.Json&version=9.0.1
|
||||||
#tool "nuget:?package=OpenCover"
|
#tool "nuget:?package=OpenCover"
|
||||||
#tool "nuget:?package=ReportGenerator"
|
#tool "nuget:?package=ReportGenerator"
|
||||||
#tool coveralls.net
|
#tool "nuget:?package=coveralls.net&version=0.7.0"
|
||||||
#addin Cake.Coveralls
|
#addin Cake.Coveralls&version=0.7.0
|
||||||
|
|
||||||
// compile
|
// compile
|
||||||
var compileConfig = Argument("configuration", "Release");
|
var compileConfig = Argument("configuration", "Release");
|
||||||
|
@ -93,7 +93,7 @@ Then map the authentication provider key to a ReRoute in your configuration e.g.
|
|||||||
Identity Server Bearer Tokens
|
Identity Server Bearer Tokens
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
In order to use IdentityServer bearer tokens register your IdentityServer services as usual in ConfigureServices with a scheme (key). If you don't understand how to do this please consul the IdentityServer documentation.
|
In order to use IdentityServer bearer tokens register your IdentityServer services as usual in ConfigureServices with a scheme (key). If you don't understand how to do this please consult the IdentityServer documentation.
|
||||||
|
|
||||||
.. code-block:: csharp
|
.. code-block:: csharp
|
||||||
|
|
||||||
|
@ -42,4 +42,4 @@ In addition to this you must add a file called peers.json to your main project a
|
|||||||
|
|
||||||
Each instance of Ocelot must have it's address in the array so that they can communicate using Rafty.
|
Each instance of Ocelot must have it's address in the array so that they can communicate using Rafty.
|
||||||
|
|
||||||
Once you have made these configuration changes you must deploy and start each instance of Ocelot using the addresses in the peers.json file. The servers should then start communicating with each other! You can test if everything is working by posting a configuration update and checking it has replicated to all servers by getting there configuration.
|
Once you have made these configuration changes you must deploy and start each instance of Ocelot using the addresses in the peers.json file. The servers should then start communicating with each other! You can test if everything is working by posting a configuration update and checking it has replicated to all servers by getting their configuration.
|
||||||
|
@ -19,7 +19,7 @@ OK so to get rate limiting working for a ReRoute you need to add the following j
|
|||||||
|
|
||||||
ClientWhitelist - This is an array that contains the whitelist of the client. It means that the client in this array will not be affected by the rate limiting.
|
ClientWhitelist - This is an array that contains the whitelist of the client. It means that the client in this array will not be affected by the rate limiting.
|
||||||
EnableRateLimiting - This value specifies enable endpoint rate limiting.
|
EnableRateLimiting - This value specifies enable endpoint rate limiting.
|
||||||
Period - This value specifies the period, such as 1s, 5m, 1h,1d and so on.
|
Period - This value specifies the period that the limit applies to, such as 1s, 5m, 1h,1d and so on. If you make more requests in the period than the limit allows then you need to wait for PeriodTimespan to elapse before you make another request.
|
||||||
PeriodTimespan - This value specifies that we can retry after a certain number of seconds.
|
PeriodTimespan - This value specifies that we can retry after a certain number of seconds.
|
||||||
Limit - This value specifies the maximum number of requests that a client can make in a defined period.
|
Limit - This value specifies the maximum number of requests that a client can make in a defined period.
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ will be used.
|
|||||||
|
|
||||||
"ServiceDiscoveryProvider": {
|
"ServiceDiscoveryProvider": {
|
||||||
"Host": "localhost",
|
"Host": "localhost",
|
||||||
"Port": 9500
|
"Port": 8500
|
||||||
}
|
}
|
||||||
|
|
||||||
In the future we can add a feature that allows ReRoute specfic configuration.
|
In the future we can add a feature that allows ReRoute specfic configuration.
|
||||||
@ -35,12 +35,29 @@ and LeastConnection algorithm you can use. If no load balancer is specified Ocel
|
|||||||
"UpstreamPathTemplate": "/posts/{postId}",
|
"UpstreamPathTemplate": "/posts/{postId}",
|
||||||
"UpstreamHttpMethod": [ "Put" ],
|
"UpstreamHttpMethod": [ "Put" ],
|
||||||
"ServiceName": "product",
|
"ServiceName": "product",
|
||||||
"LoadBalancer": "LeastConnection",
|
"LoadBalancerOptions": {
|
||||||
|
"Type": "LeastConnection"
|
||||||
|
},
|
||||||
"UseServiceDiscovery": true
|
"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.
|
||||||
|
|
||||||
|
A lot of people have asked me to implement a feature where Ocelot polls consul for latest service information rather than per request. If you want to poll consul for the latest services rather than per request (default behaviour) then you need to set the following configuration.
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
"ServiceDiscoveryProvider": {
|
||||||
|
"Host": "localhost",
|
||||||
|
"Port": 8500,
|
||||||
|
"Type": "PollConsul",
|
||||||
|
"PollingInterval": 100
|
||||||
|
}
|
||||||
|
|
||||||
|
The polling interval is in milliseconds and tells Ocelot how often to call Consul for changes in service configuration.
|
||||||
|
|
||||||
|
Please note there are tradeoffs here. If you poll Consul it is possible Ocelot will not know if a service is down depending on your polling interval and you might get more errors than if you get the latest services per request. This really depends on how volitile your services are. I doubt it will matter for most people and polling may give a tiny performance improvement over calling consul per request (as sidecar agent). If you are calling a remote consul agent then polling will be a good performance improvement.
|
||||||
|
|
||||||
ACL Token
|
ACL Token
|
||||||
---------
|
---------
|
||||||
|
|
||||||
@ -50,7 +67,7 @@ If you are using ACL with Consul Ocelot supports adding the X-Consul-Token heade
|
|||||||
|
|
||||||
"ServiceDiscoveryProvider": {
|
"ServiceDiscoveryProvider": {
|
||||||
"Host": "localhost",
|
"Host": "localhost",
|
||||||
"Port": 9500,
|
"Port": 8500,
|
||||||
"Token": "footoken"
|
"Token": "footoken"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,7 +88,7 @@ namespace Ocelot.Cache.Middleware
|
|||||||
|
|
||||||
foreach (var header in cached.ContentHeaders)
|
foreach (var header in cached.ContentHeaders)
|
||||||
{
|
{
|
||||||
streamContent.Headers.Add(header.Key, header.Value);
|
streamContent.Headers.TryAddWithoutValidation(header.Key, header.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new DownstreamResponse(streamContent, cached.StatusCode, cached.Headers.ToList());
|
return new DownstreamResponse(streamContent, cached.StatusCode, cached.Headers.ToList());
|
||||||
|
@ -7,6 +7,7 @@ namespace Ocelot.Configuration.Builder
|
|||||||
private string _type;
|
private string _type;
|
||||||
private string _token;
|
private string _token;
|
||||||
private string _configurationKey;
|
private string _configurationKey;
|
||||||
|
private int _pollingInterval;
|
||||||
|
|
||||||
public ServiceProviderConfigurationBuilder WithHost(string serviceDiscoveryProviderHost)
|
public ServiceProviderConfigurationBuilder WithHost(string serviceDiscoveryProviderHost)
|
||||||
{
|
{
|
||||||
@ -38,9 +39,15 @@ namespace Ocelot.Configuration.Builder
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ServiceProviderConfigurationBuilder WithPollingInterval(int pollingInterval)
|
||||||
|
{
|
||||||
|
_pollingInterval = pollingInterval;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public ServiceProviderConfiguration Build()
|
public ServiceProviderConfiguration Build()
|
||||||
{
|
{
|
||||||
return new ServiceProviderConfiguration(_type, _serviceDiscoveryProviderHost, _serviceDiscoveryProviderPort, _token, _configurationKey);
|
return new ServiceProviderConfiguration(_type, _serviceDiscoveryProviderHost, _serviceDiscoveryProviderPort, _token, _configurationKey, _pollingInterval);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -252,7 +252,7 @@ namespace Ocelot.Configuration.Creator
|
|||||||
return $"{nameof(CookieStickySessions)}:{fileReRoute.LoadBalancerOptions.Key}";
|
return $"{nameof(CookieStickySessions)}:{fileReRoute.LoadBalancerOptions.Key}";
|
||||||
}
|
}
|
||||||
|
|
||||||
return $"{fileReRoute.UpstreamPathTemplate}|{string.Join(",", fileReRoute.UpstreamHttpMethod)}";
|
return $"{fileReRoute.UpstreamPathTemplate}|{string.Join(",", fileReRoute.UpstreamHttpMethod)}|{string.Join(",", fileReRoute.DownstreamHostAndPorts.Select(x => $"{x.Host}:{x.Port}"))}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ namespace Ocelot.Configuration.Creator
|
|||||||
var useTracing = _tracer.GetType() != typeof(FakeServiceTracer) && options.UseTracing;
|
var useTracing = _tracer.GetType() != typeof(FakeServiceTracer) && options.UseTracing;
|
||||||
|
|
||||||
return new HttpHandlerOptions(options.AllowAutoRedirect,
|
return new HttpHandlerOptions(options.AllowAutoRedirect,
|
||||||
options.UseCookieContainer, useTracing);
|
options.UseCookieContainer, useTracing, options.UseProxy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ namespace Ocelot.Configuration.Creator
|
|||||||
{
|
{
|
||||||
var port = globalConfiguration?.ServiceDiscoveryProvider?.Port ?? 0;
|
var port = globalConfiguration?.ServiceDiscoveryProvider?.Port ?? 0;
|
||||||
var host = globalConfiguration?.ServiceDiscoveryProvider?.Host ?? "consul";
|
var host = globalConfiguration?.ServiceDiscoveryProvider?.Host ?? "consul";
|
||||||
|
var pollingInterval = globalConfiguration?.ServiceDiscoveryProvider?.PollingInterval ?? 0;
|
||||||
|
|
||||||
return new ServiceProviderConfigurationBuilder()
|
return new ServiceProviderConfigurationBuilder()
|
||||||
.WithHost(host)
|
.WithHost(host)
|
||||||
@ -16,6 +17,7 @@ namespace Ocelot.Configuration.Creator
|
|||||||
.WithType(globalConfiguration?.ServiceDiscoveryProvider?.Type)
|
.WithType(globalConfiguration?.ServiceDiscoveryProvider?.Type)
|
||||||
.WithToken(globalConfiguration?.ServiceDiscoveryProvider?.Token)
|
.WithToken(globalConfiguration?.ServiceDiscoveryProvider?.Token)
|
||||||
.WithConfigurationKey(globalConfiguration?.ServiceDiscoveryProvider?.ConfigurationKey)
|
.WithConfigurationKey(globalConfiguration?.ServiceDiscoveryProvider?.ConfigurationKey)
|
||||||
|
.WithPollingInterval(pollingInterval)
|
||||||
.Build();
|
.Build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ namespace Ocelot.Configuration.Creator
|
|||||||
{
|
{
|
||||||
public class UpstreamTemplatePatternCreator : IUpstreamTemplatePatternCreator
|
public class UpstreamTemplatePatternCreator : IUpstreamTemplatePatternCreator
|
||||||
{
|
{
|
||||||
private const string RegExMatchEverything = "[0-9a-zA-Z].*";
|
private const string RegExMatchOneOrMoreOfEverything = ".+";
|
||||||
private const string RegExMatchEndString = "$";
|
private const string RegExMatchEndString = "$";
|
||||||
private const string RegExIgnoreCase = "(?i)";
|
private const string RegExIgnoreCase = "(?i)";
|
||||||
private const string RegExForwardSlashOnly = "^/$";
|
private const string RegExForwardSlashOnly = "^/$";
|
||||||
@ -37,7 +37,7 @@ namespace Ocelot.Configuration.Creator
|
|||||||
|
|
||||||
foreach (var placeholder in placeholders)
|
foreach (var placeholder in placeholders)
|
||||||
{
|
{
|
||||||
upstreamTemplate = upstreamTemplate.Replace(placeholder, RegExMatchEverything);
|
upstreamTemplate = upstreamTemplate.Replace(placeholder, RegExMatchOneOrMoreOfEverything);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (upstreamTemplate == "/")
|
if (upstreamTemplate == "/")
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
{
|
{
|
||||||
AllowAutoRedirect = false;
|
AllowAutoRedirect = false;
|
||||||
UseCookieContainer = false;
|
UseCookieContainer = false;
|
||||||
|
UseProxy = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool AllowAutoRedirect { get; set; }
|
public bool AllowAutoRedirect { get; set; }
|
||||||
@ -13,5 +14,7 @@
|
|||||||
public bool UseCookieContainer { get; set; }
|
public bool UseCookieContainer { get; set; }
|
||||||
|
|
||||||
public bool UseTracing { get; set; }
|
public bool UseTracing { get; set; }
|
||||||
|
|
||||||
|
public bool UseProxy { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,5 +7,6 @@ namespace Ocelot.Configuration.File
|
|||||||
public string Type { get; set; }
|
public string Type { get; set; }
|
||||||
public string Token { get; set; }
|
public string Token { get; set; }
|
||||||
public string ConfigurationKey { get; set; }
|
public string ConfigurationKey { get; set; }
|
||||||
|
public int PollingInterval { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,10 +5,10 @@ using Microsoft.AspNetCore.Mvc;
|
|||||||
using Ocelot.Configuration.File;
|
using Ocelot.Configuration.File;
|
||||||
using Ocelot.Configuration.Setter;
|
using Ocelot.Configuration.Setter;
|
||||||
using Ocelot.Raft;
|
using Ocelot.Raft;
|
||||||
using Rafty.Concensus;
|
|
||||||
|
|
||||||
namespace Ocelot.Configuration
|
namespace Ocelot.Configuration
|
||||||
{
|
{
|
||||||
|
using Rafty.Concensus.Node;
|
||||||
using Repository;
|
using Repository;
|
||||||
|
|
||||||
[Authorize]
|
[Authorize]
|
||||||
@ -50,7 +50,7 @@ namespace Ocelot.Configuration
|
|||||||
{
|
{
|
||||||
var node = (INode)test;
|
var node = (INode)test;
|
||||||
var result = await node.Accept(new UpdateFileConfiguration(fileConfiguration));
|
var result = await node.Accept(new UpdateFileConfiguration(fileConfiguration));
|
||||||
if (result.GetType() == typeof(Rafty.Concensus.ErrorResponse<UpdateFileConfiguration>))
|
if (result.GetType() == typeof(Rafty.Infrastructure.ErrorResponse<UpdateFileConfiguration>))
|
||||||
{
|
{
|
||||||
return new BadRequestObjectResult("There was a problem. This error message sucks raise an issue in GitHub.");
|
return new BadRequestObjectResult("There was a problem. This error message sucks raise an issue in GitHub.");
|
||||||
}
|
}
|
||||||
|
@ -6,11 +6,12 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class HttpHandlerOptions
|
public class HttpHandlerOptions
|
||||||
{
|
{
|
||||||
public HttpHandlerOptions(bool allowAutoRedirect, bool useCookieContainer, bool useTracing)
|
public HttpHandlerOptions(bool allowAutoRedirect, bool useCookieContainer, bool useTracing, bool useProxy)
|
||||||
{
|
{
|
||||||
AllowAutoRedirect = allowAutoRedirect;
|
AllowAutoRedirect = allowAutoRedirect;
|
||||||
UseCookieContainer = useCookieContainer;
|
UseCookieContainer = useCookieContainer;
|
||||||
UseTracing = useTracing;
|
UseTracing = useTracing;
|
||||||
|
UseProxy = useProxy;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -23,9 +24,14 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool UseCookieContainer { get; private set; }
|
public bool UseCookieContainer { get; private set; }
|
||||||
|
|
||||||
// <summary>
|
/// <summary>
|
||||||
/// Specify is handler has to use a opentracing
|
/// Specify is handler has to use a opentracing
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool UseTracing { get; private set; }
|
public bool UseTracing { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Specify if handler has to use a proxy
|
||||||
|
/// </summary>
|
||||||
|
public bool UseProxy { get; private set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
private bool _allowAutoRedirect;
|
private bool _allowAutoRedirect;
|
||||||
private bool _useCookieContainer;
|
private bool _useCookieContainer;
|
||||||
private bool _useTracing;
|
private bool _useTracing;
|
||||||
|
private bool _useProxy;
|
||||||
|
|
||||||
public HttpHandlerOptionsBuilder WithAllowAutoRedirect(bool input)
|
public HttpHandlerOptionsBuilder WithAllowAutoRedirect(bool input)
|
||||||
{
|
{
|
||||||
@ -24,9 +25,15 @@
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public HttpHandlerOptionsBuilder WithUseProxy(bool useProxy)
|
||||||
|
{
|
||||||
|
_useProxy = useProxy;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public HttpHandlerOptions Build()
|
public HttpHandlerOptions Build()
|
||||||
{
|
{
|
||||||
return new HttpHandlerOptions(_allowAutoRedirect, _useCookieContainer, _useTracing);
|
return new HttpHandlerOptions(_allowAutoRedirect, _useCookieContainer, _useTracing, _useProxy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,13 +2,14 @@
|
|||||||
{
|
{
|
||||||
public class ServiceProviderConfiguration
|
public class ServiceProviderConfiguration
|
||||||
{
|
{
|
||||||
public ServiceProviderConfiguration(string type, string host, int port, string token, string configurationKey)
|
public ServiceProviderConfiguration(string type, string host, int port, string token, string configurationKey, int pollingInterval)
|
||||||
{
|
{
|
||||||
ConfigurationKey = configurationKey;
|
ConfigurationKey = configurationKey;
|
||||||
Host = host;
|
Host = host;
|
||||||
Port = port;
|
Port = port;
|
||||||
Token = token;
|
Token = token;
|
||||||
Type = type;
|
Type = type;
|
||||||
|
PollingInterval = pollingInterval;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Host { get; }
|
public string Host { get; }
|
||||||
@ -16,5 +17,6 @@
|
|||||||
public string Type { get; }
|
public string Type { get; }
|
||||||
public string Token { get; }
|
public string Token { get; }
|
||||||
public string ConfigurationKey { get; }
|
public string ConfigurationKey { get; }
|
||||||
|
public int PollingInterval { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ namespace Ocelot.DependencyInjection
|
|||||||
|
|
||||||
public static IConfigurationBuilder AddOcelot(this IConfigurationBuilder builder)
|
public static IConfigurationBuilder AddOcelot(this IConfigurationBuilder builder)
|
||||||
{
|
{
|
||||||
const string pattern = "(?i)ocelot.([a-zA-Z0-9]*).json";
|
const string pattern = "(?i)ocelot\\.([a-zA-Z0-9]*)(\\.json)$";
|
||||||
|
|
||||||
var reg = new Regex(pattern);
|
var reg = new Regex(pattern);
|
||||||
|
|
||||||
|
@ -8,6 +8,8 @@ using Rafty.Log;
|
|||||||
|
|
||||||
namespace Ocelot.DependencyInjection
|
namespace Ocelot.DependencyInjection
|
||||||
{
|
{
|
||||||
|
using Rafty.Concensus.Node;
|
||||||
|
|
||||||
public class OcelotAdministrationBuilder : IOcelotAdministrationBuilder
|
public class OcelotAdministrationBuilder : IOcelotAdministrationBuilder
|
||||||
{
|
{
|
||||||
private readonly IServiceCollection _services;
|
private readonly IServiceCollection _services;
|
||||||
@ -21,7 +23,7 @@ namespace Ocelot.DependencyInjection
|
|||||||
|
|
||||||
public IOcelotAdministrationBuilder AddRafty()
|
public IOcelotAdministrationBuilder AddRafty()
|
||||||
{
|
{
|
||||||
var settings = new InMemorySettings(4000, 5000, 100, 5000);
|
var settings = new InMemorySettings(4000, 6000, 100, 10000);
|
||||||
_services.AddSingleton<ILog, SqlLiteLog>();
|
_services.AddSingleton<ILog, SqlLiteLog>();
|
||||||
_services.AddSingleton<IFiniteStateMachine, OcelotFiniteStateMachine>();
|
_services.AddSingleton<IFiniteStateMachine, OcelotFiniteStateMachine>();
|
||||||
_services.AddSingleton<ISettings>(settings);
|
_services.AddSingleton<ISettings>(settings);
|
||||||
|
@ -45,8 +45,10 @@ namespace Ocelot.DependencyInjection
|
|||||||
using Ocelot.Infrastructure.Consul;
|
using Ocelot.Infrastructure.Consul;
|
||||||
using Butterfly.Client.Tracing;
|
using Butterfly.Client.Tracing;
|
||||||
using Ocelot.Middleware.Multiplexer;
|
using Ocelot.Middleware.Multiplexer;
|
||||||
using Pivotal.Discovery.Client;
|
|
||||||
using ServiceDiscovery.Providers;
|
using ServiceDiscovery.Providers;
|
||||||
|
using Steeltoe.Common.Discovery;
|
||||||
|
using Pivotal.Discovery.Client;
|
||||||
|
using Ocelot.Request.Creator;
|
||||||
|
|
||||||
public class OcelotBuilder : IOcelotBuilder
|
public class OcelotBuilder : IOcelotBuilder
|
||||||
{
|
{
|
||||||
@ -160,6 +162,8 @@ namespace Ocelot.DependencyInjection
|
|||||||
_services.TryAddSingleton<IConsulClientFactory, ConsulClientFactory>();
|
_services.TryAddSingleton<IConsulClientFactory, ConsulClientFactory>();
|
||||||
_services.TryAddSingleton<IResponseAggregatorFactory, InMemoryResponseAggregatorFactory>();
|
_services.TryAddSingleton<IResponseAggregatorFactory, InMemoryResponseAggregatorFactory>();
|
||||||
_services.TryAddSingleton<IDefinedAggregatorProvider, ServiceLocatorDefinedAggregatorProvider>();
|
_services.TryAddSingleton<IDefinedAggregatorProvider, ServiceLocatorDefinedAggregatorProvider>();
|
||||||
|
_services.TryAddSingleton<IDownstreamRequestCreator, DownstreamRequestCreator>();
|
||||||
|
_services.TryAddSingleton<IFrameworkDescription, FrameworkDescription>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IOcelotAdministrationBuilder AddAdministration(string path, string secret)
|
public IOcelotAdministrationBuilder AddAdministration(string path, string secret)
|
||||||
|
@ -49,7 +49,8 @@ namespace Ocelot.DownstreamRouteFinder.Finder
|
|||||||
|
|
||||||
private bool RouteIsApplicableToThisRequest(ReRoute reRoute, string httpMethod, string upstreamHost)
|
private bool RouteIsApplicableToThisRequest(ReRoute reRoute, string httpMethod, string upstreamHost)
|
||||||
{
|
{
|
||||||
return reRoute.UpstreamHttpMethod.Count == 0 || reRoute.UpstreamHttpMethod.Select(x => x.Method.ToLower()).Contains(httpMethod.ToLower()) && !(!string.IsNullOrEmpty(reRoute.UpstreamHost) && reRoute.UpstreamHost != upstreamHost);
|
return (reRoute.UpstreamHttpMethod.Count == 0 || reRoute.UpstreamHttpMethod.Select(x => x.Method.ToLower()).Contains(httpMethod.ToLower())) &&
|
||||||
|
(string.IsNullOrEmpty(reRoute.UpstreamHost) || reRoute.UpstreamHost == upstreamHost);
|
||||||
}
|
}
|
||||||
|
|
||||||
private DownstreamRoute GetPlaceholderNamesAndValues(string path, ReRoute reRoute)
|
private DownstreamRoute GetPlaceholderNamesAndValues(string path, ReRoute reRoute)
|
||||||
|
12
src/Ocelot/Infrastructure/FrameworkDescription.cs
Normal file
12
src/Ocelot/Infrastructure/FrameworkDescription.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ocelot.Infrastructure
|
||||||
|
{
|
||||||
|
public class FrameworkDescription : IFrameworkDescription
|
||||||
|
{
|
||||||
|
public string Get()
|
||||||
|
{
|
||||||
|
return RuntimeInformation.FrameworkDescription;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
7
src/Ocelot/Infrastructure/IFrameworkDescription.cs
Normal file
7
src/Ocelot/Infrastructure/IFrameworkDescription.cs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
namespace Ocelot.Infrastructure
|
||||||
|
{
|
||||||
|
public interface IFrameworkDescription
|
||||||
|
{
|
||||||
|
string Get();
|
||||||
|
}
|
||||||
|
}
|
@ -10,6 +10,7 @@ namespace Ocelot.LoadBalancer.LoadBalancers
|
|||||||
public class RoundRobin : ILoadBalancer
|
public class RoundRobin : ILoadBalancer
|
||||||
{
|
{
|
||||||
private readonly Func<Task<List<Service>>> _services;
|
private readonly Func<Task<List<Service>>> _services;
|
||||||
|
private readonly object _lock = new object();
|
||||||
|
|
||||||
private int _last;
|
private int _last;
|
||||||
|
|
||||||
@ -21,6 +22,8 @@ namespace Ocelot.LoadBalancer.LoadBalancers
|
|||||||
public async Task<Response<ServiceHostAndPort>> Lease(DownstreamContext downstreamContext)
|
public async Task<Response<ServiceHostAndPort>> Lease(DownstreamContext downstreamContext)
|
||||||
{
|
{
|
||||||
var services = await _services();
|
var services = await _services();
|
||||||
|
lock(_lock)
|
||||||
|
{
|
||||||
if (_last >= services.Count)
|
if (_last >= services.Count)
|
||||||
{
|
{
|
||||||
_last = 0;
|
_last = 0;
|
||||||
@ -30,6 +33,7 @@ namespace Ocelot.LoadBalancer.LoadBalancers
|
|||||||
_last++;
|
_last++;
|
||||||
return new OkResponse<ServiceHostAndPort>(next.HostAndPort);
|
return new OkResponse<ServiceHostAndPort>(next.HostAndPort);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void Release(ServiceHostAndPort hostAndPort)
|
public void Release(ServiceHostAndPort hostAndPort)
|
||||||
{
|
{
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
using Rafty.Infrastructure;
|
using Rafty.Infrastructure;
|
||||||
using Ocelot.Middleware.Pipeline;
|
using Ocelot.Middleware.Pipeline;
|
||||||
using Pivotal.Discovery.Client;
|
using Pivotal.Discovery.Client;
|
||||||
|
using Rafty.Concensus.Node;
|
||||||
|
|
||||||
public static class OcelotMiddlewareExtensions
|
public static class OcelotMiddlewareExtensions
|
||||||
{
|
{
|
||||||
@ -91,7 +92,7 @@
|
|||||||
applicationLifetime.ApplicationStopping.Register(() => OnShutdown(builder));
|
applicationLifetime.ApplicationStopping.Register(() => OnShutdown(builder));
|
||||||
var node = (INode)builder.ApplicationServices.GetService(typeof(INode));
|
var node = (INode)builder.ApplicationServices.GetService(typeof(INode));
|
||||||
var nodeId = (NodeId)builder.ApplicationServices.GetService(typeof(NodeId));
|
var nodeId = (NodeId)builder.ApplicationServices.GetService(typeof(NodeId));
|
||||||
node.Start(nodeId.Id);
|
node.Start(nodeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<IInternalConfiguration> CreateConfiguration(IApplicationBuilder builder)
|
private static async Task<IInternalConfiguration> CreateConfiguration(IApplicationBuilder builder)
|
||||||
|
@ -54,8 +54,8 @@
|
|||||||
<PackageReference Include="CacheManager.Microsoft.Extensions.Logging" Version="1.1.2"/>
|
<PackageReference Include="CacheManager.Microsoft.Extensions.Logging" Version="1.1.2"/>
|
||||||
<PackageReference Include="Consul" Version="0.7.2.4"/>
|
<PackageReference Include="Consul" Version="0.7.2.4"/>
|
||||||
<PackageReference Include="Polly" Version="6.0.1"/>
|
<PackageReference Include="Polly" Version="6.0.1"/>
|
||||||
<PackageReference Include="Pivotal.Discovery.Client" Version="1.1.0" />
|
|
||||||
<PackageReference Include="IdentityServer4" Version="2.2.0"/>
|
<PackageReference Include="IdentityServer4" Version="2.2.0"/>
|
||||||
<PackageReference Include="Rafty" Version="0.4.3" />
|
<PackageReference Include="Pivotal.Discovery.ClientCore" Version="2.0.1"/>
|
||||||
|
<PackageReference Include="Rafty" Version="0.4.4"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -1,36 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Rafty.FiniteStateMachine;
|
|
||||||
using Rafty.Infrastructure;
|
|
||||||
using Rafty.Log;
|
|
||||||
|
|
||||||
namespace Ocelot.Raft
|
|
||||||
{
|
|
||||||
[ExcludeFromCoverage]
|
|
||||||
public class FileFsm : IFiniteStateMachine
|
|
||||||
{
|
|
||||||
private string _id;
|
|
||||||
|
|
||||||
public FileFsm(NodeId nodeId)
|
|
||||||
{
|
|
||||||
_id = nodeId.Id.Replace("/","").Replace(":","");
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task Handle(LogEntry log)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var json = JsonConvert.SerializeObject(log.CommandData);
|
|
||||||
File.AppendAllText(_id, json);
|
|
||||||
}
|
|
||||||
catch(Exception exception)
|
|
||||||
{
|
|
||||||
Console.WriteLine(exception);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -9,6 +9,8 @@ using Rafty.Infrastructure;
|
|||||||
|
|
||||||
namespace Ocelot.Raft
|
namespace Ocelot.Raft
|
||||||
{
|
{
|
||||||
|
using Rafty.Concensus.Peers;
|
||||||
|
|
||||||
[ExcludeFromCoverage]
|
[ExcludeFromCoverage]
|
||||||
public class FilePeersProvider : IPeersProvider
|
public class FilePeersProvider : IPeersProvider
|
||||||
{
|
{
|
||||||
|
@ -10,6 +10,10 @@ using Rafty.FiniteStateMachine;
|
|||||||
|
|
||||||
namespace Ocelot.Raft
|
namespace Ocelot.Raft
|
||||||
{
|
{
|
||||||
|
using Rafty.Concensus.Messages;
|
||||||
|
using Rafty.Concensus.Peers;
|
||||||
|
using Rafty.Infrastructure;
|
||||||
|
|
||||||
[ExcludeFromCoverage]
|
[ExcludeFromCoverage]
|
||||||
public class HttpPeer : IPeer
|
public class HttpPeer : IPeer
|
||||||
{
|
{
|
||||||
|
@ -8,7 +8,7 @@ namespace Ocelot.Raft
|
|||||||
[ExcludeFromCoverage]
|
[ExcludeFromCoverage]
|
||||||
public class OcelotFiniteStateMachine : IFiniteStateMachine
|
public class OcelotFiniteStateMachine : IFiniteStateMachine
|
||||||
{
|
{
|
||||||
private IFileConfigurationSetter _setter;
|
private readonly IFileConfigurationSetter _setter;
|
||||||
|
|
||||||
public OcelotFiniteStateMachine(IFileConfigurationSetter setter)
|
public OcelotFiniteStateMachine(IFileConfigurationSetter setter)
|
||||||
{
|
{
|
||||||
|
@ -14,6 +14,9 @@ using Rafty.FiniteStateMachine;
|
|||||||
|
|
||||||
namespace Ocelot.Raft
|
namespace Ocelot.Raft
|
||||||
{
|
{
|
||||||
|
using Rafty.Concensus.Messages;
|
||||||
|
using Rafty.Concensus.Node;
|
||||||
|
|
||||||
[ExcludeFromCoverage]
|
[ExcludeFromCoverage]
|
||||||
[Authorize]
|
[Authorize]
|
||||||
[Route("raft")]
|
[Route("raft")]
|
||||||
|
@ -1,51 +1,63 @@
|
|||||||
using System.IO;
|
|
||||||
using Rafty.Log;
|
|
||||||
using Microsoft.Data.Sqlite;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using System;
|
|
||||||
using Rafty.Infrastructure;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Ocelot.Raft
|
namespace Ocelot.Raft
|
||||||
{
|
{
|
||||||
//todo - use async await
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Data.Sqlite;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Rafty.Infrastructure;
|
||||||
|
using Rafty.Log;
|
||||||
|
|
||||||
[ExcludeFromCoverage]
|
[ExcludeFromCoverage]
|
||||||
public class SqlLiteLog : ILog
|
public class SqlLiteLog : ILog
|
||||||
{
|
{
|
||||||
private string _path;
|
private readonly string _path;
|
||||||
private readonly object _lock = new object();
|
private readonly SemaphoreSlim _sempaphore = new SemaphoreSlim(1, 1);
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
private readonly NodeId _nodeId;
|
||||||
|
|
||||||
public SqlLiteLog(NodeId nodeId)
|
public SqlLiteLog(NodeId nodeId, ILoggerFactory loggerFactory)
|
||||||
{
|
{
|
||||||
|
_logger = loggerFactory.CreateLogger<SqlLiteLog>();
|
||||||
|
_nodeId = nodeId;
|
||||||
_path = $"{nodeId.Id.Replace("/", "").Replace(":", "")}.db";
|
_path = $"{nodeId.Id.Replace("/", "").Replace(":", "")}.db";
|
||||||
|
_sempaphore.Wait();
|
||||||
|
|
||||||
if (!File.Exists(_path))
|
if (!File.Exists(_path))
|
||||||
{
|
{
|
||||||
lock(_lock)
|
var fs = File.Create(_path);
|
||||||
{
|
|
||||||
FileStream fs = File.Create(_path);
|
|
||||||
fs.Dispose();
|
fs.Dispose();
|
||||||
}
|
|
||||||
|
|
||||||
using (var connection = new SqliteConnection($"Data Source={_path};"))
|
using (var connection = new SqliteConnection($"Data Source={_path};"))
|
||||||
{
|
{
|
||||||
connection.Open();
|
connection.Open();
|
||||||
var sql = @"create table logs (
|
|
||||||
|
const string sql = @"create table logs (
|
||||||
id integer primary key,
|
id integer primary key,
|
||||||
data text not null
|
data text not null
|
||||||
)";
|
)";
|
||||||
|
|
||||||
using (var command = new SqliteCommand(sql, connection))
|
using (var command = new SqliteCommand(sql, connection))
|
||||||
{
|
{
|
||||||
var result = command.ExecuteNonQuery();
|
var result = command.ExecuteNonQuery();
|
||||||
}
|
|
||||||
|
_logger.LogInformation(result == 0
|
||||||
|
? $"id: {_nodeId.Id} create database, result: {result}"
|
||||||
|
: $"id: {_nodeId.Id} did not create database., result: {result}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<int> LastLogIndex()
|
_sempaphore.Release();
|
||||||
{
|
}
|
||||||
lock(_lock)
|
|
||||||
|
public async Task<int> LastLogIndex()
|
||||||
{
|
{
|
||||||
|
_sempaphore.Wait();
|
||||||
var result = 1;
|
var result = 1;
|
||||||
using (var connection = new SqliteConnection($"Data Source={_path};"))
|
using (var connection = new SqliteConnection($"Data Source={_path};"))
|
||||||
{
|
{
|
||||||
@ -53,7 +65,7 @@ namespace Ocelot.Raft
|
|||||||
var sql = @"select id from logs order by id desc limit 1";
|
var sql = @"select id from logs order by id desc limit 1";
|
||||||
using (var command = new SqliteCommand(sql, connection))
|
using (var command = new SqliteCommand(sql, connection))
|
||||||
{
|
{
|
||||||
var index = Convert.ToInt32(command.ExecuteScalar());
|
var index = Convert.ToInt32(await command.ExecuteScalarAsync());
|
||||||
if (index > result)
|
if (index > result)
|
||||||
{
|
{
|
||||||
result = index;
|
result = index;
|
||||||
@ -61,14 +73,13 @@ namespace Ocelot.Raft
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Task.FromResult(result);
|
_sempaphore.Release();
|
||||||
}
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<long> LastLogTerm ()
|
public async Task<long> LastLogTerm()
|
||||||
{
|
|
||||||
lock(_lock)
|
|
||||||
{
|
{
|
||||||
|
_sempaphore.Wait();
|
||||||
long result = 0;
|
long result = 0;
|
||||||
using (var connection = new SqliteConnection($"Data Source={_path};"))
|
using (var connection = new SqliteConnection($"Data Source={_path};"))
|
||||||
{
|
{
|
||||||
@ -76,8 +87,9 @@ namespace Ocelot.Raft
|
|||||||
var sql = @"select data from logs order by id desc limit 1";
|
var sql = @"select data from logs order by id desc limit 1";
|
||||||
using (var command = new SqliteCommand(sql, connection))
|
using (var command = new SqliteCommand(sql, connection))
|
||||||
{
|
{
|
||||||
var data = Convert.ToString(command.ExecuteScalar());
|
var data = Convert.ToString(await command.ExecuteScalarAsync());
|
||||||
var jsonSerializerSettings = new JsonSerializerSettings() {
|
var jsonSerializerSettings = new JsonSerializerSettings()
|
||||||
|
{
|
||||||
TypeNameHandling = TypeNameHandling.All
|
TypeNameHandling = TypeNameHandling.All
|
||||||
};
|
};
|
||||||
var log = JsonConvert.DeserializeObject<LogEntry>(data, jsonSerializerSettings);
|
var log = JsonConvert.DeserializeObject<LogEntry>(data, jsonSerializerSettings);
|
||||||
@ -87,15 +99,13 @@ namespace Ocelot.Raft
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_sempaphore.Release();
|
||||||
return Task.FromResult(result);
|
return result;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<int> Count ()
|
public async Task<int> Count()
|
||||||
{
|
|
||||||
lock(_lock)
|
|
||||||
{
|
{
|
||||||
|
_sempaphore.Wait();
|
||||||
var result = 0;
|
var result = 0;
|
||||||
using (var connection = new SqliteConnection($"Data Source={_path};"))
|
using (var connection = new SqliteConnection($"Data Source={_path};"))
|
||||||
{
|
{
|
||||||
@ -103,152 +113,183 @@ namespace Ocelot.Raft
|
|||||||
var sql = @"select count(id) from logs";
|
var sql = @"select count(id) from logs";
|
||||||
using (var command = new SqliteCommand(sql, connection))
|
using (var command = new SqliteCommand(sql, connection))
|
||||||
{
|
{
|
||||||
var index = Convert.ToInt32(command.ExecuteScalar());
|
var index = Convert.ToInt32(await command.ExecuteScalarAsync());
|
||||||
if (index > result)
|
if (index > result)
|
||||||
{
|
{
|
||||||
result = index;
|
result = index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_sempaphore.Release();
|
||||||
return Task.FromResult(result);
|
return result;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<int> Apply(LogEntry log)
|
public async Task<int> Apply(LogEntry log)
|
||||||
{
|
|
||||||
lock(_lock)
|
|
||||||
{
|
{
|
||||||
|
_sempaphore.Wait();
|
||||||
using (var connection = new SqliteConnection($"Data Source={_path};"))
|
using (var connection = new SqliteConnection($"Data Source={_path};"))
|
||||||
{
|
{
|
||||||
connection.Open();
|
connection.Open();
|
||||||
var jsonSerializerSettings = new JsonSerializerSettings() {
|
var jsonSerializerSettings = new JsonSerializerSettings()
|
||||||
|
{
|
||||||
TypeNameHandling = TypeNameHandling.All
|
TypeNameHandling = TypeNameHandling.All
|
||||||
};
|
};
|
||||||
var data = JsonConvert.SerializeObject(log, jsonSerializerSettings);
|
var data = JsonConvert.SerializeObject(log, jsonSerializerSettings);
|
||||||
|
|
||||||
//todo - sql injection dont copy this..
|
//todo - sql injection dont copy this..
|
||||||
var sql = $"insert into logs (data) values ('{data}')";
|
var sql = $"insert into logs (data) values ('{data}')";
|
||||||
|
_logger.LogInformation($"id: {_nodeId.Id}, sql: {sql}");
|
||||||
using (var command = new SqliteCommand(sql, connection))
|
using (var command = new SqliteCommand(sql, connection))
|
||||||
{
|
{
|
||||||
var result = command.ExecuteNonQuery();
|
var result = await command.ExecuteNonQueryAsync();
|
||||||
|
_logger.LogInformation($"id: {_nodeId.Id}, insert log result: {result}");
|
||||||
}
|
}
|
||||||
|
|
||||||
sql = "select last_insert_rowid()";
|
sql = "select last_insert_rowid()";
|
||||||
using (var command = new SqliteCommand(sql, connection))
|
using (var command = new SqliteCommand(sql, connection))
|
||||||
{
|
{
|
||||||
var result = command.ExecuteScalar();
|
var result = await command.ExecuteScalarAsync();
|
||||||
return Task.FromResult(Convert.ToInt32(result));
|
_logger.LogInformation($"id: {_nodeId.Id}, about to release semaphore");
|
||||||
}
|
_sempaphore.Release();
|
||||||
|
_logger.LogInformation($"id: {_nodeId.Id}, saved log to sqlite");
|
||||||
|
return Convert.ToInt32(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task DeleteConflictsFromThisLog(int index, LogEntry logEntry)
|
public async Task DeleteConflictsFromThisLog(int index, LogEntry logEntry)
|
||||||
{
|
|
||||||
lock(_lock)
|
|
||||||
{
|
{
|
||||||
|
_sempaphore.Wait();
|
||||||
using (var connection = new SqliteConnection($"Data Source={_path};"))
|
using (var connection = new SqliteConnection($"Data Source={_path};"))
|
||||||
{
|
{
|
||||||
connection.Open();
|
connection.Open();
|
||||||
|
|
||||||
//todo - sql injection dont copy this..
|
//todo - sql injection dont copy this..
|
||||||
var sql = $"select data from logs where id = {index};";
|
var sql = $"select data from logs where id = {index};";
|
||||||
|
_logger.LogInformation($"id: {_nodeId.Id} sql: {sql}");
|
||||||
using (var command = new SqliteCommand(sql, connection))
|
using (var command = new SqliteCommand(sql, connection))
|
||||||
{
|
{
|
||||||
var data = Convert.ToString(command.ExecuteScalar());
|
var data = Convert.ToString(await command.ExecuteScalarAsync());
|
||||||
var jsonSerializerSettings = new JsonSerializerSettings() {
|
var jsonSerializerSettings = new JsonSerializerSettings()
|
||||||
|
{
|
||||||
TypeNameHandling = TypeNameHandling.All
|
TypeNameHandling = TypeNameHandling.All
|
||||||
};
|
};
|
||||||
|
|
||||||
|
_logger.LogInformation($"id {_nodeId.Id} got log for index: {index}, data is {data} and new log term is {logEntry.Term}");
|
||||||
|
|
||||||
var log = JsonConvert.DeserializeObject<LogEntry>(data, jsonSerializerSettings);
|
var log = JsonConvert.DeserializeObject<LogEntry>(data, jsonSerializerSettings);
|
||||||
if (logEntry != null && log != null && logEntry.Term != log.Term)
|
if (logEntry != null && log != null && logEntry.Term != log.Term)
|
||||||
{
|
{
|
||||||
//todo - sql injection dont copy this..
|
//todo - sql injection dont copy this..
|
||||||
var deleteSql = $"delete from logs where id >= {index};";
|
var deleteSql = $"delete from logs where id >= {index};";
|
||||||
|
_logger.LogInformation($"id: {_nodeId.Id} sql: {deleteSql}");
|
||||||
using (var deleteCommand = new SqliteCommand(deleteSql, connection))
|
using (var deleteCommand = new SqliteCommand(deleteSql, connection))
|
||||||
{
|
{
|
||||||
var result = deleteCommand.ExecuteNonQuery();
|
var result = await deleteCommand.ExecuteNonQueryAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_sempaphore.Release();
|
||||||
}
|
}
|
||||||
|
|
||||||
return Task.CompletedTask;
|
public async Task<bool> IsDuplicate(int index, LogEntry logEntry)
|
||||||
}
|
|
||||||
|
|
||||||
public Task<LogEntry> Get(int index)
|
|
||||||
{
|
|
||||||
lock(_lock)
|
|
||||||
{
|
{
|
||||||
|
_sempaphore.Wait();
|
||||||
using (var connection = new SqliteConnection($"Data Source={_path};"))
|
using (var connection = new SqliteConnection($"Data Source={_path};"))
|
||||||
{
|
{
|
||||||
connection.Open();
|
connection.Open();
|
||||||
|
//todo - sql injection dont copy this..
|
||||||
|
var sql = $"select data from logs where id = {index};";
|
||||||
|
using (var command = new SqliteCommand(sql, connection))
|
||||||
|
{
|
||||||
|
var data = Convert.ToString(await command.ExecuteScalarAsync());
|
||||||
|
var jsonSerializerSettings = new JsonSerializerSettings()
|
||||||
|
{
|
||||||
|
TypeNameHandling = TypeNameHandling.All
|
||||||
|
};
|
||||||
|
|
||||||
|
var log = JsonConvert.DeserializeObject<LogEntry>(data, jsonSerializerSettings);
|
||||||
|
|
||||||
|
if (logEntry != null && log != null && logEntry.Term == log.Term)
|
||||||
|
{
|
||||||
|
_sempaphore.Release();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_sempaphore.Release();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<LogEntry> Get(int index)
|
||||||
|
{
|
||||||
|
_sempaphore.Wait();
|
||||||
|
using (var connection = new SqliteConnection($"Data Source={_path};"))
|
||||||
|
{
|
||||||
|
connection.Open();
|
||||||
//todo - sql injection dont copy this..
|
//todo - sql injection dont copy this..
|
||||||
var sql = $"select data from logs where id = {index}";
|
var sql = $"select data from logs where id = {index}";
|
||||||
using (var command = new SqliteCommand(sql, connection))
|
using (var command = new SqliteCommand(sql, connection))
|
||||||
{
|
{
|
||||||
var data = Convert.ToString(command.ExecuteScalar());
|
var data = Convert.ToString(await command.ExecuteScalarAsync());
|
||||||
var jsonSerializerSettings = new JsonSerializerSettings() {
|
var jsonSerializerSettings = new JsonSerializerSettings()
|
||||||
|
{
|
||||||
TypeNameHandling = TypeNameHandling.All
|
TypeNameHandling = TypeNameHandling.All
|
||||||
};
|
};
|
||||||
var log = JsonConvert.DeserializeObject<LogEntry>(data, jsonSerializerSettings);
|
var log = JsonConvert.DeserializeObject<LogEntry>(data, jsonSerializerSettings);
|
||||||
return Task.FromResult(log);
|
_sempaphore.Release();
|
||||||
}
|
return log;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<List<(int index, LogEntry logEntry)>> GetFrom(int index)
|
public async Task<List<(int index, LogEntry logEntry)>> GetFrom(int index)
|
||||||
{
|
|
||||||
lock(_lock)
|
|
||||||
{
|
{
|
||||||
|
_sempaphore.Wait();
|
||||||
var logsToReturn = new List<(int, LogEntry)>();
|
var logsToReturn = new List<(int, LogEntry)>();
|
||||||
|
|
||||||
using (var connection = new SqliteConnection($"Data Source={_path};"))
|
using (var connection = new SqliteConnection($"Data Source={_path};"))
|
||||||
{
|
{
|
||||||
connection.Open();
|
connection.Open();
|
||||||
|
|
||||||
//todo - sql injection dont copy this..
|
//todo - sql injection dont copy this..
|
||||||
var sql = $"select id, data from logs where id >= {index}";
|
var sql = $"select id, data from logs where id >= {index}";
|
||||||
using (var command = new SqliteCommand(sql, connection))
|
using (var command = new SqliteCommand(sql, connection))
|
||||||
{
|
{
|
||||||
using(var reader = command.ExecuteReader())
|
using (var reader = await command.ExecuteReaderAsync())
|
||||||
{
|
{
|
||||||
while (reader.Read())
|
while (reader.Read())
|
||||||
{
|
{
|
||||||
var id = Convert.ToInt32(reader[0]);
|
var id = Convert.ToInt32(reader[0]);
|
||||||
var data = (string)reader[1];
|
var data = (string)reader[1];
|
||||||
var jsonSerializerSettings = new JsonSerializerSettings() {
|
var jsonSerializerSettings = new JsonSerializerSettings()
|
||||||
|
{
|
||||||
TypeNameHandling = TypeNameHandling.All
|
TypeNameHandling = TypeNameHandling.All
|
||||||
};
|
};
|
||||||
var log = JsonConvert.DeserializeObject<LogEntry>(data, jsonSerializerSettings);
|
var log = JsonConvert.DeserializeObject<LogEntry>(data, jsonSerializerSettings);
|
||||||
logsToReturn.Add((id, log));
|
logsToReturn.Add((id, log));
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_sempaphore.Release();
|
||||||
|
return logsToReturn;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Task.FromResult(logsToReturn);
|
public async Task<long> GetTermAtIndex(int index)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<long> GetTermAtIndex(int index)
|
|
||||||
{
|
|
||||||
lock(_lock)
|
|
||||||
{
|
{
|
||||||
|
_sempaphore.Wait();
|
||||||
long result = 0;
|
long result = 0;
|
||||||
using (var connection = new SqliteConnection($"Data Source={_path};"))
|
using (var connection = new SqliteConnection($"Data Source={_path};"))
|
||||||
{
|
{
|
||||||
connection.Open();
|
connection.Open();
|
||||||
|
|
||||||
//todo - sql injection dont copy this..
|
//todo - sql injection dont copy this..
|
||||||
var sql = $"select data from logs where id = {index}";
|
var sql = $"select data from logs where id = {index}";
|
||||||
using (var command = new SqliteCommand(sql, connection))
|
using (var command = new SqliteCommand(sql, connection))
|
||||||
{
|
{
|
||||||
var data = Convert.ToString(command.ExecuteScalar());
|
var data = Convert.ToString(await command.ExecuteScalarAsync());
|
||||||
var jsonSerializerSettings = new JsonSerializerSettings() {
|
var jsonSerializerSettings = new JsonSerializerSettings()
|
||||||
|
{
|
||||||
TypeNameHandling = TypeNameHandling.All
|
TypeNameHandling = TypeNameHandling.All
|
||||||
};
|
};
|
||||||
var log = JsonConvert.DeserializeObject<LogEntry>(data, jsonSerializerSettings);
|
var log = JsonConvert.DeserializeObject<LogEntry>(data, jsonSerializerSettings);
|
||||||
@ -258,29 +299,24 @@ namespace Ocelot.Raft
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_sempaphore.Release();
|
||||||
return Task.FromResult(result);
|
return result;
|
||||||
}
|
}
|
||||||
}
|
public async Task Remove(int indexOfCommand)
|
||||||
|
|
||||||
public Task Remove(int indexOfCommand)
|
|
||||||
{
|
|
||||||
lock(_lock)
|
|
||||||
{
|
{
|
||||||
|
_sempaphore.Wait();
|
||||||
using (var connection = new SqliteConnection($"Data Source={_path};"))
|
using (var connection = new SqliteConnection($"Data Source={_path};"))
|
||||||
{
|
{
|
||||||
connection.Open();
|
connection.Open();
|
||||||
|
|
||||||
//todo - sql injection dont copy this..
|
//todo - sql injection dont copy this..
|
||||||
var deleteSql = $"delete from logs where id >= {indexOfCommand};";
|
var deleteSql = $"delete from logs where id >= {indexOfCommand};";
|
||||||
|
_logger.LogInformation($"id: {_nodeId.Id} Remove {deleteSql}");
|
||||||
using (var deleteCommand = new SqliteCommand(deleteSql, connection))
|
using (var deleteCommand = new SqliteCommand(deleteSql, connection))
|
||||||
{
|
{
|
||||||
var result = deleteCommand.ExecuteNonQuery();
|
var result = await deleteCommand.ExecuteNonQueryAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
_sempaphore.Release();
|
||||||
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ namespace Ocelot.RateLimit
|
|||||||
if (entry.HasValue)
|
if (entry.HasValue)
|
||||||
{
|
{
|
||||||
// entry has not expired
|
// entry has not expired
|
||||||
if (entry.Value.Timestamp + ConvertToTimeSpan(rule.Period) >= DateTime.UtcNow)
|
if (entry.Value.Timestamp + TimeSpan.FromSeconds(rule.PeriodTimespan) >= DateTime.UtcNow)
|
||||||
{
|
{
|
||||||
// increment request count
|
// increment request count
|
||||||
var totalRequests = entry.Value.TotalRequests + 1;
|
var totalRequests = entry.Value.TotalRequests + 1;
|
||||||
|
42
src/Ocelot/Request/Creator/DownstreamRequestCreator.cs
Normal file
42
src/Ocelot/Request/Creator/DownstreamRequestCreator.cs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
namespace Ocelot.Request.Creator
|
||||||
|
{
|
||||||
|
using System.Net.Http;
|
||||||
|
using Ocelot.Request.Middleware;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using Ocelot.Infrastructure;
|
||||||
|
|
||||||
|
public class DownstreamRequestCreator : IDownstreamRequestCreator
|
||||||
|
{
|
||||||
|
private readonly IFrameworkDescription _framework;
|
||||||
|
private const string dotNetFramework = ".NET Framework";
|
||||||
|
|
||||||
|
public DownstreamRequestCreator(IFrameworkDescription framework)
|
||||||
|
{
|
||||||
|
_framework = framework;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DownstreamRequest Create(HttpRequestMessage request)
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* According to https://tools.ietf.org/html/rfc7231
|
||||||
|
* GET,HEAD,DELETE,CONNECT,TRACE
|
||||||
|
* Can have body but server can reject the request.
|
||||||
|
* And MS HttpClient in Full Framework actually rejects it.
|
||||||
|
* see #366 issue
|
||||||
|
**/
|
||||||
|
|
||||||
|
if(_framework.Get().Contains(dotNetFramework))
|
||||||
|
{
|
||||||
|
if (request.Method == HttpMethod.Get ||
|
||||||
|
request.Method == HttpMethod.Head ||
|
||||||
|
request.Method == HttpMethod.Delete ||
|
||||||
|
request.Method == HttpMethod.Trace)
|
||||||
|
{
|
||||||
|
request.Content = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new DownstreamRequest(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
10
src/Ocelot/Request/Creator/IDownstreamRequestCreator.cs
Normal file
10
src/Ocelot/Request/Creator/IDownstreamRequestCreator.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
namespace Ocelot.Request.Creator
|
||||||
|
{
|
||||||
|
using System.Net.Http;
|
||||||
|
using Ocelot.Request.Middleware;
|
||||||
|
|
||||||
|
public interface IDownstreamRequestCreator
|
||||||
|
{
|
||||||
|
DownstreamRequest Create(HttpRequestMessage request);
|
||||||
|
}
|
||||||
|
}
|
@ -1,25 +1,28 @@
|
|||||||
namespace Ocelot.Request.Middleware
|
namespace Ocelot.Request.Middleware
|
||||||
{
|
{
|
||||||
using System.Net.Http;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Ocelot.DownstreamRouteFinder.Middleware;
|
using Ocelot.DownstreamRouteFinder.Middleware;
|
||||||
using Ocelot.Infrastructure.RequestData;
|
using Ocelot.Infrastructure.RequestData;
|
||||||
using Ocelot.Logging;
|
using Ocelot.Logging;
|
||||||
using Ocelot.Middleware;
|
using Ocelot.Middleware;
|
||||||
|
using Ocelot.Request.Creator;
|
||||||
|
|
||||||
public class DownstreamRequestInitialiserMiddleware : OcelotMiddleware
|
public class DownstreamRequestInitialiserMiddleware : OcelotMiddleware
|
||||||
{
|
{
|
||||||
private readonly OcelotRequestDelegate _next;
|
private readonly OcelotRequestDelegate _next;
|
||||||
private readonly Mapper.IRequestMapper _requestMapper;
|
private readonly Mapper.IRequestMapper _requestMapper;
|
||||||
|
private readonly IDownstreamRequestCreator _creator;
|
||||||
|
|
||||||
public DownstreamRequestInitialiserMiddleware(OcelotRequestDelegate next,
|
public DownstreamRequestInitialiserMiddleware(OcelotRequestDelegate next,
|
||||||
IOcelotLoggerFactory loggerFactory,
|
IOcelotLoggerFactory loggerFactory,
|
||||||
Mapper.IRequestMapper requestMapper)
|
Mapper.IRequestMapper requestMapper,
|
||||||
|
IDownstreamRequestCreator creator)
|
||||||
:base(loggerFactory.CreateLogger<DownstreamRequestInitialiserMiddleware>())
|
:base(loggerFactory.CreateLogger<DownstreamRequestInitialiserMiddleware>())
|
||||||
{
|
{
|
||||||
_next = next;
|
_next = next;
|
||||||
_requestMapper = requestMapper;
|
_requestMapper = requestMapper;
|
||||||
|
_creator = creator;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Invoke(DownstreamContext context)
|
public async Task Invoke(DownstreamContext context)
|
||||||
@ -31,7 +34,7 @@ namespace Ocelot.Request.Middleware
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
context.DownstreamRequest = new DownstreamRequest(downstreamRequest.Data);
|
context.DownstreamRequest = _creator.Create(downstreamRequest.Data);
|
||||||
|
|
||||||
await _next.Invoke(context);
|
await _next.Invoke(context);
|
||||||
}
|
}
|
||||||
|
@ -43,16 +43,11 @@ namespace Ocelot.Requester
|
|||||||
return httpClient;
|
return httpClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
var httpclientHandler = new HttpClientHandler
|
var handler = CreateHandler(context);
|
||||||
{
|
|
||||||
AllowAutoRedirect = context.DownstreamReRoute.HttpHandlerOptions.AllowAutoRedirect,
|
|
||||||
UseCookies = context.DownstreamReRoute.HttpHandlerOptions.UseCookieContainer,
|
|
||||||
CookieContainer = new CookieContainer()
|
|
||||||
};
|
|
||||||
|
|
||||||
if (context.DownstreamReRoute.DangerousAcceptAnyServerCertificateValidator)
|
if (context.DownstreamReRoute.DangerousAcceptAnyServerCertificateValidator)
|
||||||
{
|
{
|
||||||
httpclientHandler.ServerCertificateCustomValidationCallback = (request, certificate, chain, errors) => true;
|
handler.ServerCertificateCustomValidationCallback = (request, certificate, chain, errors) => true;
|
||||||
|
|
||||||
_logger
|
_logger
|
||||||
.LogWarning($"You have ignored all SSL warnings by using DangerousAcceptAnyServerCertificateValidator for this DownstreamReRoute, UpstreamPathTemplate: {context.DownstreamReRoute.UpstreamPathTemplate}, DownstreamPathTemplate: {context.DownstreamReRoute.DownstreamPathTemplate}");
|
.LogWarning($"You have ignored all SSL warnings by using DangerousAcceptAnyServerCertificateValidator for this DownstreamReRoute, UpstreamPathTemplate: {context.DownstreamReRoute.UpstreamPathTemplate}, DownstreamPathTemplate: {context.DownstreamReRoute.DownstreamPathTemplate}");
|
||||||
@ -62,7 +57,7 @@ namespace Ocelot.Requester
|
|||||||
? _defaultTimeout
|
? _defaultTimeout
|
||||||
: TimeSpan.FromMilliseconds(context.DownstreamReRoute.QosOptions.TimeoutValue);
|
: TimeSpan.FromMilliseconds(context.DownstreamReRoute.QosOptions.TimeoutValue);
|
||||||
|
|
||||||
_httpClient = new HttpClient(CreateHttpMessageHandler(httpclientHandler, context.DownstreamReRoute))
|
_httpClient = new HttpClient(CreateHttpMessageHandler(handler, context.DownstreamReRoute))
|
||||||
{
|
{
|
||||||
Timeout = timeout
|
Timeout = timeout
|
||||||
};
|
};
|
||||||
@ -72,6 +67,43 @@ namespace Ocelot.Requester
|
|||||||
return _client;
|
return _client;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private HttpClientHandler CreateHandler(DownstreamContext context)
|
||||||
|
{
|
||||||
|
// Dont' create the CookieContainer if UseCookies is not set or the HttpClient will complain
|
||||||
|
// under .Net Full Framework
|
||||||
|
bool useCookies = context.DownstreamReRoute.HttpHandlerOptions.UseCookieContainer;
|
||||||
|
|
||||||
|
if (useCookies)
|
||||||
|
{
|
||||||
|
return UseCookiesHandler(context);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return UseNonCookiesHandler(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private HttpClientHandler UseNonCookiesHandler(DownstreamContext context)
|
||||||
|
{
|
||||||
|
return new HttpClientHandler
|
||||||
|
{
|
||||||
|
AllowAutoRedirect = context.DownstreamReRoute.HttpHandlerOptions.AllowAutoRedirect,
|
||||||
|
UseCookies = context.DownstreamReRoute.HttpHandlerOptions.UseCookieContainer,
|
||||||
|
UseProxy = context.DownstreamReRoute.HttpHandlerOptions.UseProxy
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private HttpClientHandler UseCookiesHandler(DownstreamContext context)
|
||||||
|
{
|
||||||
|
return new HttpClientHandler
|
||||||
|
{
|
||||||
|
AllowAutoRedirect = context.DownstreamReRoute.HttpHandlerOptions.AllowAutoRedirect,
|
||||||
|
UseCookies = context.DownstreamReRoute.HttpHandlerOptions.UseCookieContainer,
|
||||||
|
UseProxy = context.DownstreamReRoute.HttpHandlerOptions.UseProxy,
|
||||||
|
CookieContainer = new CookieContainer()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public void Save()
|
public void Save()
|
||||||
{
|
{
|
||||||
_cacheHandlers.Set(_cacheKey, _client, TimeSpan.FromHours(24));
|
_cacheHandlers.Set(_cacheKey, _client, TimeSpan.FromHours(24));
|
||||||
|
@ -59,12 +59,8 @@ namespace Ocelot.Responder
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void SetErrorResponseOnContext(HttpContext context, int statusCode)
|
public void SetErrorResponseOnContext(HttpContext context, int statusCode)
|
||||||
{
|
|
||||||
context.Response.OnStarting(x =>
|
|
||||||
{
|
{
|
||||||
context.Response.StatusCode = statusCode;
|
context.Response.StatusCode = statusCode;
|
||||||
return Task.CompletedTask;
|
|
||||||
}, context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AddHeaderIfDoesntExist(HttpContext context, Header httpResponseHeader)
|
private static void AddHeaderIfDoesntExist(HttpContext context, Header httpResponseHeader)
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Pivotal.Discovery.Client;
|
using Steeltoe.Common.Discovery;
|
||||||
using Values;
|
using Values;
|
||||||
|
|
||||||
public class EurekaServiceDiscoveryProvider : IServiceDiscoveryProvider
|
public class EurekaServiceDiscoveryProvider : IServiceDiscoveryProvider
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
{
|
{
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Pivotal.Discovery.Client;
|
using Steeltoe.Common.Discovery;
|
||||||
|
|
||||||
public class FakeEurekaDiscoveryClient : IDiscoveryClient
|
public class FakeEurekaDiscoveryClient : IDiscoveryClient
|
||||||
{
|
{
|
||||||
|
@ -0,0 +1,56 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Consul;
|
||||||
|
using Ocelot.Infrastructure.Consul;
|
||||||
|
using Ocelot.Infrastructure.Extensions;
|
||||||
|
using Ocelot.Logging;
|
||||||
|
using Ocelot.ServiceDiscovery.Configuration;
|
||||||
|
using Ocelot.Values;
|
||||||
|
|
||||||
|
namespace Ocelot.ServiceDiscovery.Providers
|
||||||
|
{
|
||||||
|
public class PollingConsulServiceDiscoveryProvider : IServiceDiscoveryProvider
|
||||||
|
{
|
||||||
|
private readonly IOcelotLogger _logger;
|
||||||
|
private readonly IServiceDiscoveryProvider _consulServiceDiscoveryProvider;
|
||||||
|
private readonly Timer _timer;
|
||||||
|
private bool _polling;
|
||||||
|
private List<Service> _services;
|
||||||
|
private string _keyOfServiceInConsul;
|
||||||
|
|
||||||
|
public PollingConsulServiceDiscoveryProvider(int pollingInterval, string keyOfServiceInConsul, IOcelotLoggerFactory factory, IServiceDiscoveryProvider consulServiceDiscoveryProvider)
|
||||||
|
{;
|
||||||
|
_logger = factory.CreateLogger<PollingConsulServiceDiscoveryProvider>();
|
||||||
|
_keyOfServiceInConsul = keyOfServiceInConsul;
|
||||||
|
_consulServiceDiscoveryProvider = consulServiceDiscoveryProvider;
|
||||||
|
_services = new List<Service>();
|
||||||
|
|
||||||
|
_timer = new Timer(async x =>
|
||||||
|
{
|
||||||
|
if(_polling)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_polling = true;
|
||||||
|
await Poll();
|
||||||
|
_polling = false;
|
||||||
|
|
||||||
|
}, null, pollingInterval, pollingInterval);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<List<Service>> Get()
|
||||||
|
{
|
||||||
|
return Task.FromResult(_services);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Poll()
|
||||||
|
{
|
||||||
|
_services = await _consulServiceDiscoveryProvider.Get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -8,7 +8,7 @@ using Ocelot.Values;
|
|||||||
|
|
||||||
namespace Ocelot.ServiceDiscovery
|
namespace Ocelot.ServiceDiscovery
|
||||||
{
|
{
|
||||||
using Pivotal.Discovery.Client;
|
using Steeltoe.Common.Discovery;
|
||||||
|
|
||||||
public class ServiceDiscoveryProviderFactory : IServiceDiscoveryProviderFactory
|
public class ServiceDiscoveryProviderFactory : IServiceDiscoveryProviderFactory
|
||||||
{
|
{
|
||||||
@ -56,7 +56,15 @@ namespace Ocelot.ServiceDiscovery
|
|||||||
}
|
}
|
||||||
|
|
||||||
var consulRegistryConfiguration = new ConsulRegistryConfiguration(serviceConfig.Host, serviceConfig.Port, serviceName, serviceConfig.Token);
|
var consulRegistryConfiguration = new ConsulRegistryConfiguration(serviceConfig.Host, serviceConfig.Port, serviceName, serviceConfig.Token);
|
||||||
return new ConsulServiceDiscoveryProvider(consulRegistryConfiguration, _factory, _consulFactory);
|
|
||||||
|
var consulServiceDiscoveryProvider = new ConsulServiceDiscoveryProvider(consulRegistryConfiguration, _factory, _consulFactory);
|
||||||
|
|
||||||
|
if (serviceConfig.Type?.ToLower() == "pollconsul")
|
||||||
|
{
|
||||||
|
return new PollingConsulServiceDiscoveryProvider(serviceConfig.PollingInterval, consulRegistryConfiguration.KeyOfServiceInConsul, _factory, consulServiceDiscoveryProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
return consulServiceDiscoveryProvider;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51899", 200, "Hello from Laura"))
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51899", 200, "Hello from Laura", null, null))
|
||||||
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
.And(x => _steps.GivenOcelotIsRunning())
|
.And(x => _steps.GivenOcelotIsRunning())
|
||||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
@ -65,6 +65,50 @@ namespace Ocelot.AcceptanceTests
|
|||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_cached_response_with_expires_header()
|
||||||
|
{
|
||||||
|
var configuration = new FileConfiguration
|
||||||
|
{
|
||||||
|
ReRoutes = new List<FileReRoute>
|
||||||
|
{
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamPathTemplate = "/",
|
||||||
|
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||||
|
{
|
||||||
|
new FileHostAndPort
|
||||||
|
{
|
||||||
|
Host = "localhost",
|
||||||
|
Port = 52839,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
DownstreamScheme = "http",
|
||||||
|
UpstreamPathTemplate = "/",
|
||||||
|
UpstreamHttpMethod = new List<string> { "Get" },
|
||||||
|
FileCacheOptions = new FileCacheOptions
|
||||||
|
{
|
||||||
|
TtlSeconds = 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:52839", 200, "Hello from Laura", "Expires", "-1"))
|
||||||
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
|
.And(x => _steps.GivenOcelotIsRunning())
|
||||||
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
|
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
||||||
|
.Given(x => x.GivenTheServiceNowReturns("http://localhost:52839", 200, "Hello from Tom"))
|
||||||
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
|
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
||||||
|
.And(x => _steps.ThenTheContentLengthIs(16))
|
||||||
|
.And(x => _steps.ThenTheResponseBodyHeaderIs("Expires", "-1"))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void should_return_cached_response_when_using_jsonserialized_cache()
|
public void should_return_cached_response_when_using_jsonserialized_cache()
|
||||||
{
|
{
|
||||||
@ -94,7 +138,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51899", 200, "Hello from Laura"))
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51899", 200, "Hello from Laura", null, null))
|
||||||
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
.And(x => _steps.GivenOcelotIsRunningUsingJsonSerializedCache())
|
.And(x => _steps.GivenOcelotIsRunningUsingJsonSerializedCache())
|
||||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
@ -136,7 +180,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51899", 200, "Hello from Laura"))
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51899", 200, "Hello from Laura", null, null))
|
||||||
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
.And(x => _steps.GivenOcelotIsRunning())
|
.And(x => _steps.GivenOcelotIsRunning())
|
||||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
@ -158,10 +202,10 @@ namespace Ocelot.AcceptanceTests
|
|||||||
private void GivenTheServiceNowReturns(string url, int statusCode, string responseBody)
|
private void GivenTheServiceNowReturns(string url, int statusCode, string responseBody)
|
||||||
{
|
{
|
||||||
_builder.Dispose();
|
_builder.Dispose();
|
||||||
GivenThereIsAServiceRunningOn(url, statusCode, responseBody);
|
GivenThereIsAServiceRunningOn(url, statusCode, responseBody, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GivenThereIsAServiceRunningOn(string url, int statusCode, string responseBody)
|
private void GivenThereIsAServiceRunningOn(string url, int statusCode, string responseBody, string key, string value)
|
||||||
{
|
{
|
||||||
_builder = new WebHostBuilder()
|
_builder = new WebHostBuilder()
|
||||||
.UseUrls(url)
|
.UseUrls(url)
|
||||||
@ -173,6 +217,10 @@ namespace Ocelot.AcceptanceTests
|
|||||||
{
|
{
|
||||||
app.Run(async context =>
|
app.Run(async context =>
|
||||||
{
|
{
|
||||||
|
if(!string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(key))
|
||||||
|
{
|
||||||
|
context.Response.Headers.Add(key, value);
|
||||||
|
}
|
||||||
context.Response.StatusCode = statusCode;
|
context.Response.StatusCode = statusCode;
|
||||||
await context.Response.WriteAsync(responseBody);
|
await context.Response.WriteAsync(responseBody);
|
||||||
});
|
});
|
||||||
|
@ -90,6 +90,71 @@ namespace Ocelot.AcceptanceTests
|
|||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_wait_for_period_timespan_to_elapse_before_making_next_request()
|
||||||
|
{
|
||||||
|
var configuration = new FileConfiguration
|
||||||
|
{
|
||||||
|
ReRoutes = new List<FileReRoute>
|
||||||
|
{
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamPathTemplate = "/api/ClientRateLimit",
|
||||||
|
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||||
|
{
|
||||||
|
new FileHostAndPort
|
||||||
|
{
|
||||||
|
Host = "localhost",
|
||||||
|
Port = 51926,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
DownstreamScheme = "http",
|
||||||
|
UpstreamPathTemplate = "/api/ClientRateLimit",
|
||||||
|
UpstreamHttpMethod = new List<string> { "Get" },
|
||||||
|
RequestIdKey = _steps.RequestIdKey,
|
||||||
|
|
||||||
|
RateLimitOptions = new FileRateLimitRule()
|
||||||
|
{
|
||||||
|
EnableRateLimiting = true,
|
||||||
|
ClientWhitelist = new List<string>(),
|
||||||
|
Limit = 3,
|
||||||
|
Period = "1s",
|
||||||
|
PeriodTimespan = 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
GlobalConfiguration = new FileGlobalConfiguration()
|
||||||
|
{
|
||||||
|
RateLimitOptions = new FileRateLimitOptions()
|
||||||
|
{
|
||||||
|
ClientIdHeader = "ClientId",
|
||||||
|
DisableRateLimitHeaders = false,
|
||||||
|
QuotaExceededMessage = "",
|
||||||
|
RateLimitCounterPrefix = "",
|
||||||
|
HttpStatusCode = 428
|
||||||
|
},
|
||||||
|
RequestIdKey ="oceclientrequest"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51926", "/api/ClientRateLimit"))
|
||||||
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
|
.And(x => _steps.GivenOcelotIsRunning())
|
||||||
|
.When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimesForRateLimit("/api/ClientRateLimit",1))
|
||||||
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(200))
|
||||||
|
.When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimesForRateLimit("/api/ClientRateLimit", 2))
|
||||||
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(200))
|
||||||
|
.When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimesForRateLimit("/api/ClientRateLimit",1))
|
||||||
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(428))
|
||||||
|
.And(x => _steps.GivenIWait(1000))
|
||||||
|
.When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimesForRateLimit("/api/ClientRateLimit",1))
|
||||||
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(428))
|
||||||
|
.And(x => _steps.GivenIWait(1000))
|
||||||
|
.When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimesForRateLimit("/api/ClientRateLimit",1))
|
||||||
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(200))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void should_call_middleware_withWhitelistClient()
|
public void should_call_middleware_withWhitelistClient()
|
||||||
{
|
{
|
||||||
|
@ -185,6 +185,52 @@ namespace Ocelot.AcceptanceTests
|
|||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_fix_issue_417()
|
||||||
|
{
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
GlobalConfiguration = new FileGlobalConfiguration
|
||||||
|
{
|
||||||
|
BaseUrl = "http://anotherapp.azurewebsites.net"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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://anotherapp.azurewebsites.net/pay/Receive"))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void request_should_reuse_cookies_with_cookie_container()
|
public void request_should_reuse_cookies_with_cookie_container()
|
||||||
{
|
{
|
||||||
@ -342,7 +388,8 @@ namespace Ocelot.AcceptanceTests
|
|||||||
app.UsePathBase(basePath);
|
app.UsePathBase(basePath);
|
||||||
app.Run(context =>
|
app.Run(context =>
|
||||||
{
|
{
|
||||||
context.Response.OnStarting(() => {
|
context.Response.OnStarting(() =>
|
||||||
|
{
|
||||||
context.Response.Headers.Add(headerKey, headerValue);
|
context.Response.Headers.Add(headerKey, headerValue);
|
||||||
context.Response.StatusCode = statusCode;
|
context.Response.StatusCode = statusCode;
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
|
@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Builder;
|
|||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Ocelot.Configuration.File;
|
using Ocelot.Configuration.File;
|
||||||
|
using Ocelot.LoadBalancer.LoadBalancers;
|
||||||
using Shouldly;
|
using Shouldly;
|
||||||
using TestStack.BDDfy;
|
using TestStack.BDDfy;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
@ -26,7 +27,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void should_load_balance_request()
|
public void should_load_balance_request_with_least_connection()
|
||||||
{
|
{
|
||||||
var downstreamServiceOneUrl = "http://localhost:50881";
|
var downstreamServiceOneUrl = "http://localhost:50881";
|
||||||
var downstreamServiceTwoUrl = "http://localhost:50892";
|
var downstreamServiceTwoUrl = "http://localhost:50892";
|
||||||
@ -41,7 +42,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
UpstreamPathTemplate = "/",
|
UpstreamPathTemplate = "/",
|
||||||
UpstreamHttpMethod = new List<string> { "Get" },
|
UpstreamHttpMethod = new List<string> { "Get" },
|
||||||
LoadBalancerOptions = new FileLoadBalancerOptions { Type = "LeastConnection" },
|
LoadBalancerOptions = new FileLoadBalancerOptions { Type = nameof(LeastConnection) },
|
||||||
DownstreamHostAndPorts = new List<FileHostAndPort>
|
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||||
{
|
{
|
||||||
new FileHostAndPort
|
new FileHostAndPort
|
||||||
@ -72,6 +73,55 @@ namespace Ocelot.AcceptanceTests
|
|||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_load_balance_request_with_round_robin()
|
||||||
|
{
|
||||||
|
var downstreamPortOne = 51881;
|
||||||
|
var downstreamPortTwo = 51892;
|
||||||
|
var downstreamServiceOneUrl = $"http://localhost:{downstreamPortOne}";
|
||||||
|
var downstreamServiceTwoUrl = $"http://localhost:{downstreamPortTwo}";
|
||||||
|
|
||||||
|
var configuration = new FileConfiguration
|
||||||
|
{
|
||||||
|
ReRoutes = new List<FileReRoute>
|
||||||
|
{
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamPathTemplate = "/",
|
||||||
|
DownstreamScheme = "http",
|
||||||
|
UpstreamPathTemplate = "/",
|
||||||
|
UpstreamHttpMethod = new List<string> { "Get" },
|
||||||
|
LoadBalancerOptions = new FileLoadBalancerOptions { Type = nameof(RoundRobin) },
|
||||||
|
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||||
|
{
|
||||||
|
new FileHostAndPort
|
||||||
|
{
|
||||||
|
Host = "localhost",
|
||||||
|
Port = downstreamPortOne
|
||||||
|
},
|
||||||
|
new FileHostAndPort
|
||||||
|
{
|
||||||
|
Host = "localhost",
|
||||||
|
Port = downstreamPortTwo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
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 ThenBothServicesCalledRealisticAmountOfTimes(int bottom, int top)
|
private void ThenBothServicesCalledRealisticAmountOfTimes(int bottom, int top)
|
||||||
{
|
{
|
||||||
_counterOne.ShouldBeInRange(bottom, top);
|
_counterOne.ShouldBeInRange(bottom, top);
|
||||||
|
@ -55,6 +55,6 @@
|
|||||||
<PackageReference Include="Consul" Version="0.7.2.4" />
|
<PackageReference Include="Consul" Version="0.7.2.4" />
|
||||||
<PackageReference Include="xunit" Version="2.3.1" />
|
<PackageReference Include="xunit" Version="2.3.1" />
|
||||||
<PackageReference Include="Butterfly.Client.AspNetCore" Version="0.0.8" />
|
<PackageReference Include="Butterfly.Client.AspNetCore" Version="0.0.8" />
|
||||||
<PackageReference Include="Rafty" Version="0.4.3" />
|
<PackageReference Include="Rafty" Version="0.4.4" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -14,7 +14,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
using TestStack.BDDfy;
|
using TestStack.BDDfy;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Pivotal.Discovery.Client;
|
using Steeltoe.Common.Discovery;
|
||||||
|
|
||||||
public class ServiceDiscoveryTests : IDisposable
|
public class ServiceDiscoveryTests : IDisposable
|
||||||
{
|
{
|
||||||
@ -458,6 +458,64 @@ namespace Ocelot.AcceptanceTests
|
|||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_handle_request_to_poll_consul_for_downstream_service_and_make_request()
|
||||||
|
{
|
||||||
|
const int consulPort = 8518;
|
||||||
|
const string serviceName = "web";
|
||||||
|
const int downstreamServicePort = 8082;
|
||||||
|
var downstreamServiceOneUrl = $"http://localhost:{downstreamServicePort}";
|
||||||
|
var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}";
|
||||||
|
var serviceEntryOne = new ServiceEntry()
|
||||||
|
{
|
||||||
|
Service = new AgentService()
|
||||||
|
{
|
||||||
|
Service = serviceName,
|
||||||
|
Address = "localhost",
|
||||||
|
Port = downstreamServicePort,
|
||||||
|
ID = $"web_90_0_2_224_{downstreamServicePort}",
|
||||||
|
Tags = new[] {"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,
|
||||||
|
LoadBalancerOptions = new FileLoadBalancerOptions { Type = "LeastConnection" },
|
||||||
|
UseServiceDiscovery = true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
GlobalConfiguration = new FileGlobalConfiguration()
|
||||||
|
{
|
||||||
|
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider()
|
||||||
|
{
|
||||||
|
Host = "localhost",
|
||||||
|
Port = consulPort,
|
||||||
|
Type = "PollConsul",
|
||||||
|
PollingInterval = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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.WhenIGetUrlOnTheApiGatewayWaitingForTheResponseToBeOk("/home"))
|
||||||
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
|
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
private void WhenIAddAServiceBackIn(ServiceEntry serviceEntryTwo)
|
private void WhenIAddAServiceBackIn(ServiceEntry serviceEntryTwo)
|
||||||
{
|
{
|
||||||
_consulServices.Add(serviceEntryTwo);
|
_consulServices.Add(serviceEntryTwo);
|
||||||
|
@ -27,6 +27,7 @@ using System.Text;
|
|||||||
using static Ocelot.AcceptanceTests.HttpDelegatingHandlersTests;
|
using static Ocelot.AcceptanceTests.HttpDelegatingHandlersTests;
|
||||||
using Ocelot.Requester;
|
using Ocelot.Requester;
|
||||||
using Ocelot.Middleware.Multiplexer;
|
using Ocelot.Middleware.Multiplexer;
|
||||||
|
using static Ocelot.Infrastructure.Wait;
|
||||||
|
|
||||||
namespace Ocelot.AcceptanceTests
|
namespace Ocelot.AcceptanceTests
|
||||||
{
|
{
|
||||||
@ -182,6 +183,11 @@ namespace Ocelot.AcceptanceTests
|
|||||||
_ocelotClient = _ocelotServer.CreateClient();
|
_ocelotClient = _ocelotServer.CreateClient();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal void GivenIWait(int wait)
|
||||||
|
{
|
||||||
|
Thread.Sleep(wait);
|
||||||
|
}
|
||||||
|
|
||||||
public void GivenOcelotIsRunningWithMiddleareBeforePipeline<T>(Func<object, Task> callback)
|
public void GivenOcelotIsRunningWithMiddleareBeforePipeline<T>(Func<object, Task> callback)
|
||||||
{
|
{
|
||||||
_webHostBuilder = new WebHostBuilder();
|
_webHostBuilder = new WebHostBuilder();
|
||||||
@ -386,6 +392,12 @@ namespace Ocelot.AcceptanceTests
|
|||||||
header.First().ShouldBe(value);
|
header.First().ShouldBe(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ThenTheResponseBodyHeaderIs(string key, string value)
|
||||||
|
{
|
||||||
|
var header = _response.Content.Headers.GetValues(key);
|
||||||
|
header.First().ShouldBe(value);
|
||||||
|
}
|
||||||
|
|
||||||
public void ThenTheTraceHeaderIsSet(string key)
|
public void ThenTheTraceHeaderIsSet(string key)
|
||||||
{
|
{
|
||||||
var header = _response.Headers.GetValues(key);
|
var header = _response.Headers.GetValues(key);
|
||||||
@ -675,6 +687,24 @@ namespace Ocelot.AcceptanceTests
|
|||||||
_response = _ocelotClient.GetAsync(url).Result;
|
_response = _ocelotClient.GetAsync(url).Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void WhenIGetUrlOnTheApiGatewayWaitingForTheResponseToBeOk(string url)
|
||||||
|
{
|
||||||
|
var result = WaitFor(2000).Until(() => {
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_response = _ocelotClient.GetAsync(url).Result;
|
||||||
|
_response.EnsureSuccessStatusCode();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch(Exception)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
result.ShouldBeTrue();
|
||||||
|
}
|
||||||
|
|
||||||
public void WhenIGetUrlOnTheApiGateway(string url, string cookie, string value)
|
public void WhenIGetUrlOnTheApiGateway(string url, string cookie, string value)
|
||||||
{
|
{
|
||||||
var request = _ocelotServer.CreateRequest(url);
|
var request = _ocelotServer.CreateRequest(url);
|
||||||
|
@ -11,6 +11,7 @@ using IdentityServer4.Models;
|
|||||||
using IdentityServer4.Test;
|
using IdentityServer4.Test;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
@ -38,6 +39,8 @@ namespace Ocelot.IntegrationTests
|
|||||||
private IWebHostBuilder _webHostBuilderTwo;
|
private IWebHostBuilder _webHostBuilderTwo;
|
||||||
private IWebHost _builderTwo;
|
private IWebHost _builderTwo;
|
||||||
private IWebHost _identityServerBuilder;
|
private IWebHost _identityServerBuilder;
|
||||||
|
private IWebHost _fooServiceBuilder;
|
||||||
|
private IWebHost _barServiceBuilder;
|
||||||
|
|
||||||
public AdministrationTests()
|
public AdministrationTests()
|
||||||
{
|
{
|
||||||
@ -276,6 +279,80 @@ namespace Ocelot.IntegrationTests
|
|||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_get_file_configuration_edit_and_post_updated_version_redirecting_reroute()
|
||||||
|
{
|
||||||
|
var fooPort = 47689;
|
||||||
|
var barPort = 47690;
|
||||||
|
|
||||||
|
var initialConfiguration = new FileConfiguration
|
||||||
|
{
|
||||||
|
ReRoutes = new List<FileReRoute>()
|
||||||
|
{
|
||||||
|
new FileReRoute()
|
||||||
|
{
|
||||||
|
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||||
|
{
|
||||||
|
new FileHostAndPort
|
||||||
|
{
|
||||||
|
Host = "localhost",
|
||||||
|
Port = fooPort,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
DownstreamScheme = "http",
|
||||||
|
DownstreamPathTemplate = "/foo",
|
||||||
|
UpstreamHttpMethod = new List<string> { "get" },
|
||||||
|
UpstreamPathTemplate = "/foo"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var updatedConfiguration = new FileConfiguration
|
||||||
|
{
|
||||||
|
GlobalConfiguration = new FileGlobalConfiguration
|
||||||
|
{
|
||||||
|
},
|
||||||
|
ReRoutes = new List<FileReRoute>()
|
||||||
|
{
|
||||||
|
new FileReRoute()
|
||||||
|
{
|
||||||
|
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||||
|
{
|
||||||
|
new FileHostAndPort
|
||||||
|
{
|
||||||
|
Host = "localhost",
|
||||||
|
Port = barPort,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
DownstreamScheme = "http",
|
||||||
|
DownstreamPathTemplate = "/bar",
|
||||||
|
UpstreamHttpMethod = new List<string> { "get" },
|
||||||
|
UpstreamPathTemplate = "/foo"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.Given(x => GivenThereIsAConfiguration(initialConfiguration))
|
||||||
|
.And(x => GivenThereIsAFooServiceRunningOn($"http://localhost:{fooPort}"))
|
||||||
|
.And(x => GivenThereIsABarServiceRunningOn($"http://localhost:{barPort}"))
|
||||||
|
.And(x => GivenOcelotIsRunning())
|
||||||
|
.And(x => WhenIGetUrlOnTheApiGateway("/foo"))
|
||||||
|
.Then(x => ThenTheResponseBodyShouldBe("foo"))
|
||||||
|
.And(x => GivenIHaveAnOcelotToken("/administration"))
|
||||||
|
.And(x => GivenIHaveAddedATokenToMyRequest())
|
||||||
|
.When(x => WhenIPostOnTheApiGateway("/administration/configuration", updatedConfiguration))
|
||||||
|
.Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
|
.And(x => ThenTheResponseShouldBe(updatedConfiguration))
|
||||||
|
.And(x => WhenIGetUrlOnTheApiGateway("/foo"))
|
||||||
|
.Then(x => ThenTheResponseBodyShouldBe("bar"))
|
||||||
|
.When(x => WhenIPostOnTheApiGateway("/administration/configuration", initialConfiguration))
|
||||||
|
.Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
|
.And(x => ThenTheResponseShouldBe(initialConfiguration))
|
||||||
|
.And(x => WhenIGetUrlOnTheApiGateway("/foo"))
|
||||||
|
.Then(x => ThenTheResponseBodyShouldBe("foo"))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void should_clear_region()
|
public void should_clear_region()
|
||||||
{
|
{
|
||||||
@ -516,6 +593,12 @@ namespace Ocelot.IntegrationTests
|
|||||||
result.Value.ShouldBe(expected);
|
result.Value.ShouldBe(expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ThenTheResponseBodyShouldBe(string expected)
|
||||||
|
{
|
||||||
|
var content = _response.Content.ReadAsStringAsync().Result;
|
||||||
|
content.ShouldBe(expected);
|
||||||
|
}
|
||||||
|
|
||||||
private void ThenTheResponseShouldBe(FileConfiguration expecteds)
|
private void ThenTheResponseShouldBe(FileConfiguration expecteds)
|
||||||
{
|
{
|
||||||
var response = JsonConvert.DeserializeObject<FileConfiguration>(_response.Content.ReadAsStringAsync().Result);
|
var response = JsonConvert.DeserializeObject<FileConfiguration>(_response.Content.ReadAsStringAsync().Result);
|
||||||
@ -723,5 +806,47 @@ namespace Ocelot.IntegrationTests
|
|||||||
_httpClient?.Dispose();
|
_httpClient?.Dispose();
|
||||||
_identityServerBuilder?.Dispose();
|
_identityServerBuilder?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void GivenThereIsAFooServiceRunningOn(string baseUrl)
|
||||||
|
{
|
||||||
|
_fooServiceBuilder = new WebHostBuilder()
|
||||||
|
.UseUrls(baseUrl)
|
||||||
|
.UseKestrel()
|
||||||
|
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||||
|
.UseIISIntegration()
|
||||||
|
.Configure(app =>
|
||||||
|
{
|
||||||
|
app.UsePathBase("/foo");
|
||||||
|
app.Run(async context =>
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 200;
|
||||||
|
await context.Response.WriteAsync("foo");
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
_fooServiceBuilder.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenThereIsABarServiceRunningOn(string baseUrl)
|
||||||
|
{
|
||||||
|
_barServiceBuilder = new WebHostBuilder()
|
||||||
|
.UseUrls(baseUrl)
|
||||||
|
.UseKestrel()
|
||||||
|
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||||
|
.UseIISIntegration()
|
||||||
|
.Configure(app =>
|
||||||
|
{
|
||||||
|
app.UsePathBase("/bar");
|
||||||
|
app.Run(async context =>
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 200;
|
||||||
|
await context.Response.WriteAsync("bar");
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
_barServiceBuilder.Start();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,7 @@
|
|||||||
<PackageReference Include="Shouldly" Version="3.0.0" />
|
<PackageReference Include="Shouldly" Version="3.0.0" />
|
||||||
<PackageReference Include="TestStack.BDDfy" Version="4.3.2" />
|
<PackageReference Include="TestStack.BDDfy" Version="4.3.2" />
|
||||||
<PackageReference Include="Consul" Version="0.7.2.4" />
|
<PackageReference Include="Consul" Version="0.7.2.4" />
|
||||||
<PackageReference Include="Rafty" Version="0.4.3" />
|
<PackageReference Include="Rafty" Version="0.4.4" />
|
||||||
<PackageReference Include="Microsoft.Data.SQLite" Version="2.0.1" />
|
<PackageReference Include="Microsoft.Data.SQLite" Version="2.0.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
@ -23,6 +23,7 @@ using Ocelot.Middleware;
|
|||||||
|
|
||||||
namespace Ocelot.IntegrationTests
|
namespace Ocelot.IntegrationTests
|
||||||
{
|
{
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Xunit.Abstractions;
|
using Xunit.Abstractions;
|
||||||
|
|
||||||
public class RaftTests : IDisposable
|
public class RaftTests : IDisposable
|
||||||
@ -31,7 +32,7 @@ namespace Ocelot.IntegrationTests
|
|||||||
private readonly List<IWebHostBuilder> _webHostBuilders;
|
private readonly List<IWebHostBuilder> _webHostBuilders;
|
||||||
private readonly List<Thread> _threads;
|
private readonly List<Thread> _threads;
|
||||||
private FilePeers _peers;
|
private FilePeers _peers;
|
||||||
private readonly HttpClient _httpClient;
|
private HttpClient _httpClient;
|
||||||
private readonly HttpClient _httpClientForAssertions;
|
private readonly HttpClient _httpClientForAssertions;
|
||||||
private BearerToken _token;
|
private BearerToken _token;
|
||||||
private HttpResponseMessage _response;
|
private HttpResponseMessage _response;
|
||||||
@ -42,17 +43,27 @@ namespace Ocelot.IntegrationTests
|
|||||||
{
|
{
|
||||||
_output = output;
|
_output = output;
|
||||||
_httpClientForAssertions = new HttpClient();
|
_httpClientForAssertions = new HttpClient();
|
||||||
_httpClient = new HttpClient();
|
|
||||||
var ocelotBaseUrl = "http://localhost:5000";
|
|
||||||
_httpClient.BaseAddress = new Uri(ocelotBaseUrl);
|
|
||||||
_webHostBuilders = new List<IWebHostBuilder>();
|
_webHostBuilders = new List<IWebHostBuilder>();
|
||||||
_builders = new List<IWebHost>();
|
_builders = new List<IWebHost>();
|
||||||
_threads = new List<Thread>();
|
_threads = new List<Thread>();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact(Skip = "still broken waiting for work in rafty")]
|
[Fact(Skip = "Still not stable, more work required in rafty..")]
|
||||||
public void should_persist_command_to_five_servers()
|
public async Task should_persist_command_to_five_servers()
|
||||||
{
|
{
|
||||||
|
var peers = new List<FilePeer>
|
||||||
|
{
|
||||||
|
new FilePeer {HostAndPort = "http://localhost:5000"},
|
||||||
|
|
||||||
|
new FilePeer {HostAndPort = "http://localhost:5001"},
|
||||||
|
|
||||||
|
new FilePeer {HostAndPort = "http://localhost:5002"},
|
||||||
|
|
||||||
|
new FilePeer {HostAndPort = "http://localhost:5003"},
|
||||||
|
|
||||||
|
new FilePeer {HostAndPort = "http://localhost:5004"}
|
||||||
|
};
|
||||||
|
|
||||||
var configuration = new FileConfiguration
|
var configuration = new FileConfiguration
|
||||||
{
|
{
|
||||||
GlobalConfiguration = new FileGlobalConfiguration
|
GlobalConfiguration = new FileGlobalConfiguration
|
||||||
@ -101,17 +112,31 @@ namespace Ocelot.IntegrationTests
|
|||||||
};
|
};
|
||||||
|
|
||||||
var command = new UpdateFileConfiguration(updatedConfiguration);
|
var command = new UpdateFileConfiguration(updatedConfiguration);
|
||||||
|
GivenThePeersAre(peers);
|
||||||
GivenThereIsAConfiguration(configuration);
|
GivenThereIsAConfiguration(configuration);
|
||||||
GivenFiveServersAreRunning();
|
GivenFiveServersAreRunning();
|
||||||
GivenIHaveAnOcelotToken("/administration");
|
await GivenIHaveAnOcelotToken("/administration");
|
||||||
WhenISendACommandIntoTheCluster(command);
|
await WhenISendACommandIntoTheCluster(command);
|
||||||
Thread.Sleep(5000);
|
Thread.Sleep(5000);
|
||||||
ThenTheCommandIsReplicatedToAllStateMachines(command);
|
await ThenTheCommandIsReplicatedToAllStateMachines(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact(Skip = "still broken waiting for work in rafty")]
|
[Fact(Skip = "Still not stable, more work required in rafty..")]
|
||||||
public void should_persist_command_to_five_servers_when_using_administration_api()
|
public async Task should_persist_command_to_five_servers_when_using_administration_api()
|
||||||
{
|
{
|
||||||
|
var peers = new List<FilePeer>
|
||||||
|
{
|
||||||
|
new FilePeer {HostAndPort = "http://localhost:5005"},
|
||||||
|
|
||||||
|
new FilePeer {HostAndPort = "http://localhost:5006"},
|
||||||
|
|
||||||
|
new FilePeer {HostAndPort = "http://localhost:5007"},
|
||||||
|
|
||||||
|
new FilePeer {HostAndPort = "http://localhost:5008"},
|
||||||
|
|
||||||
|
new FilePeer {HostAndPort = "http://localhost:5009"}
|
||||||
|
};
|
||||||
|
|
||||||
var configuration = new FileConfiguration
|
var configuration = new FileConfiguration
|
||||||
{
|
{
|
||||||
};
|
};
|
||||||
@ -154,17 +179,29 @@ namespace Ocelot.IntegrationTests
|
|||||||
};
|
};
|
||||||
|
|
||||||
var command = new UpdateFileConfiguration(updatedConfiguration);
|
var command = new UpdateFileConfiguration(updatedConfiguration);
|
||||||
|
GivenThePeersAre(peers);
|
||||||
GivenThereIsAConfiguration(configuration);
|
GivenThereIsAConfiguration(configuration);
|
||||||
GivenFiveServersAreRunning();
|
GivenFiveServersAreRunning();
|
||||||
GivenIHaveAnOcelotToken("/administration");
|
await GivenIHaveAnOcelotToken("/administration");
|
||||||
GivenIHaveAddedATokenToMyRequest();
|
GivenIHaveAddedATokenToMyRequest();
|
||||||
WhenIPostOnTheApiGateway("/administration/configuration", updatedConfiguration);
|
await WhenIPostOnTheApiGateway("/administration/configuration", updatedConfiguration);
|
||||||
ThenTheCommandIsReplicatedToAllStateMachines(command);
|
await ThenTheCommandIsReplicatedToAllStateMachines(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WhenISendACommandIntoTheCluster(UpdateFileConfiguration command)
|
private void GivenThePeersAre(List<FilePeer> peers)
|
||||||
{
|
{
|
||||||
bool SendCommand()
|
FilePeers filePeers = new FilePeers();
|
||||||
|
filePeers.Peers.AddRange(peers);
|
||||||
|
var json = JsonConvert.SerializeObject(filePeers);
|
||||||
|
File.WriteAllText("peers.json", json);
|
||||||
|
_httpClient = new HttpClient();
|
||||||
|
var ocelotBaseUrl = peers[0].HostAndPort;
|
||||||
|
_httpClient.BaseAddress = new Uri(ocelotBaseUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task WhenISendACommandIntoTheCluster(UpdateFileConfiguration command)
|
||||||
|
{
|
||||||
|
async Task<bool> SendCommand()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -174,13 +211,13 @@ namespace Ocelot.IntegrationTests
|
|||||||
TypeNameHandling = TypeNameHandling.All
|
TypeNameHandling = TypeNameHandling.All
|
||||||
});
|
});
|
||||||
var httpContent = new StringContent(json);
|
var httpContent = new StringContent(json);
|
||||||
httpContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
|
httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
|
||||||
using (var httpClient = new HttpClient())
|
using (var httpClient = new HttpClient())
|
||||||
{
|
{
|
||||||
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token.AccessToken);
|
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token.AccessToken);
|
||||||
var response = httpClient.PostAsync($"{p.HostAndPort}/administration/raft/command", httpContent).GetAwaiter().GetResult();
|
var response = await httpClient.PostAsync($"{p.HostAndPort}/administration/raft/command", httpContent);
|
||||||
response.EnsureSuccessStatusCode();
|
response.EnsureSuccessStatusCode();
|
||||||
var content = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
|
var content = await response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
var errorResult = JsonConvert.DeserializeObject<ErrorResponse<UpdateFileConfiguration>>(content);
|
var errorResult = JsonConvert.DeserializeObject<ErrorResponse<UpdateFileConfiguration>>(content);
|
||||||
|
|
||||||
@ -206,13 +243,19 @@ namespace Ocelot.IntegrationTests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var commandSent = WaitFor(20000).Until(() => SendCommand());
|
var commandSent = await WaitFor(40000).Until(async () =>
|
||||||
|
{
|
||||||
|
var result = await SendCommand();
|
||||||
|
Thread.Sleep(1000);
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
|
||||||
commandSent.ShouldBeTrue();
|
commandSent.ShouldBeTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ThenTheCommandIsReplicatedToAllStateMachines(UpdateFileConfiguration expecteds)
|
private async Task ThenTheCommandIsReplicatedToAllStateMachines(UpdateFileConfiguration expecteds)
|
||||||
{
|
{
|
||||||
bool CommandCalledOnAllStateMachines()
|
async Task<bool> CommandCalledOnAllStateMachines()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -232,8 +275,8 @@ namespace Ocelot.IntegrationTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
_httpClientForAssertions.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token.AccessToken);
|
_httpClientForAssertions.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token.AccessToken);
|
||||||
var result = _httpClientForAssertions.GetAsync($"{peer.HostAndPort}/administration/configuration").Result;
|
var result = await _httpClientForAssertions.GetAsync($"{peer.HostAndPort}/administration/configuration");
|
||||||
var json = result.Content.ReadAsStringAsync().Result;
|
var json = await result.Content.ReadAsStringAsync();
|
||||||
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(expecteds.Configuration.GlobalConfiguration.RequestIdKey);
|
response.GlobalConfiguration.RequestIdKey.ShouldBe(expecteds.Configuration.GlobalConfiguration.RequestIdKey);
|
||||||
response.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expecteds.Configuration.GlobalConfiguration.ServiceDiscoveryProvider.Host);
|
response.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expecteds.Configuration.GlobalConfiguration.ServiceDiscoveryProvider.Host);
|
||||||
@ -268,19 +311,29 @@ namespace Ocelot.IntegrationTests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var commandOnAllStateMachines = WaitFor(20000).Until(() => CommandCalledOnAllStateMachines());
|
var commandOnAllStateMachines = await WaitFor(40000).Until(async () =>
|
||||||
|
{
|
||||||
|
var result = await CommandCalledOnAllStateMachines();
|
||||||
|
Thread.Sleep(1000);
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
|
||||||
commandOnAllStateMachines.ShouldBeTrue();
|
commandOnAllStateMachines.ShouldBeTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WhenIPostOnTheApiGateway(string url, FileConfiguration updatedConfiguration)
|
private async Task WhenIPostOnTheApiGateway(string url, FileConfiguration updatedConfiguration)
|
||||||
{
|
{
|
||||||
bool SendCommand()
|
async Task<bool> SendCommand()
|
||||||
{
|
{
|
||||||
var json = JsonConvert.SerializeObject(updatedConfiguration);
|
var json = JsonConvert.SerializeObject(updatedConfiguration);
|
||||||
|
|
||||||
var content = new StringContent(json);
|
var content = new StringContent(json);
|
||||||
|
|
||||||
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
|
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
|
||||||
_response = _httpClient.PostAsync(url, content).Result;
|
|
||||||
var responseContent = _response.Content.ReadAsStringAsync().Result;
|
_response = await _httpClient.PostAsync(url, content);
|
||||||
|
|
||||||
|
var responseContent = await _response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
if(responseContent == "There was a problem. This error message sucks raise an issue in GitHub.")
|
if(responseContent == "There was a problem. This error message sucks raise an issue in GitHub.")
|
||||||
{
|
{
|
||||||
@ -295,7 +348,13 @@ namespace Ocelot.IntegrationTests
|
|||||||
return _response.IsSuccessStatusCode;
|
return _response.IsSuccessStatusCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
var commandSent = WaitFor(20000).Until(() => SendCommand());
|
var commandSent = await WaitFor(40000).Until(async () =>
|
||||||
|
{
|
||||||
|
var result = await SendCommand();
|
||||||
|
Thread.Sleep(1000);
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
|
||||||
commandSent.ShouldBeTrue();
|
commandSent.ShouldBeTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -304,9 +363,9 @@ namespace Ocelot.IntegrationTests
|
|||||||
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token.AccessToken);
|
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token.AccessToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GivenIHaveAnOcelotToken(string adminPath)
|
private async Task GivenIHaveAnOcelotToken(string adminPath)
|
||||||
{
|
{
|
||||||
bool AddToken()
|
async Task<bool> AddToken()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -320,8 +379,8 @@ namespace Ocelot.IntegrationTests
|
|||||||
};
|
};
|
||||||
var content = new FormUrlEncodedContent(formData);
|
var content = new FormUrlEncodedContent(formData);
|
||||||
|
|
||||||
var response = _httpClient.PostAsync(tokenUrl, content).Result;
|
var response = await _httpClient.PostAsync(tokenUrl, content);
|
||||||
var responseContent = response.Content.ReadAsStringAsync().Result;
|
var responseContent = await response.Content.ReadAsStringAsync();
|
||||||
if(!response.IsSuccessStatusCode)
|
if(!response.IsSuccessStatusCode)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@ -329,7 +388,7 @@ namespace Ocelot.IntegrationTests
|
|||||||
|
|
||||||
_token = JsonConvert.DeserializeObject<BearerToken>(responseContent);
|
_token = JsonConvert.DeserializeObject<BearerToken>(responseContent);
|
||||||
var configPath = $"{adminPath}/.well-known/openid-configuration";
|
var configPath = $"{adminPath}/.well-known/openid-configuration";
|
||||||
response = _httpClient.GetAsync(configPath).Result;
|
response = await _httpClient.GetAsync(configPath);
|
||||||
return response.IsSuccessStatusCode;
|
return response.IsSuccessStatusCode;
|
||||||
}
|
}
|
||||||
catch(Exception)
|
catch(Exception)
|
||||||
@ -338,7 +397,13 @@ namespace Ocelot.IntegrationTests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var addToken = WaitFor(20000).Until(() => AddToken());
|
var addToken = await WaitFor(40000).Until(async () =>
|
||||||
|
{
|
||||||
|
var result = await AddToken();
|
||||||
|
Thread.Sleep(1000);
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
|
||||||
addToken.ShouldBeTrue();
|
addToken.ShouldBeTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,7 +114,8 @@
|
|||||||
"HttpHandlerOptions": {
|
"HttpHandlerOptions": {
|
||||||
"AllowAutoRedirect": true,
|
"AllowAutoRedirect": true,
|
||||||
"UseCookieContainer": true,
|
"UseCookieContainer": true,
|
||||||
"UseTracing": true
|
"UseTracing": true,
|
||||||
|
"UseProxy": true
|
||||||
},
|
},
|
||||||
"QoSOptions": {
|
"QoSOptions": {
|
||||||
"ExceptionsAllowedBeforeBreaking": 3,
|
"ExceptionsAllowedBeforeBreaking": 3,
|
||||||
|
@ -63,10 +63,26 @@
|
|||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_returned_cached_item_when_it_is_in_cache_expires_header()
|
||||||
|
{
|
||||||
|
var contentHeaders = new Dictionary<string, IEnumerable<string>>
|
||||||
|
{
|
||||||
|
{ "Expires", new List<string> { "-1" } }
|
||||||
|
};
|
||||||
|
|
||||||
|
var cachedResponse = new CachedResponse(HttpStatusCode.OK, new Dictionary<string, IEnumerable<string>>(), "", contentHeaders);
|
||||||
|
this.Given(x => x.GivenThereIsACachedResponse(cachedResponse))
|
||||||
|
.And(x => x.GivenTheDownstreamRouteIs())
|
||||||
|
.When(x => x.WhenICallTheMiddleware())
|
||||||
|
.Then(x => x.ThenTheCacheGetIsCalledCorrectly())
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void should_continue_with_pipeline_and_cache_response()
|
public void should_continue_with_pipeline_and_cache_response()
|
||||||
{
|
{
|
||||||
this.Given(x => x.GivenResponseIsNotCached())
|
this.Given(x => x.GivenResponseIsNotCached(new HttpResponseMessage()))
|
||||||
.And(x => x.GivenTheDownstreamRouteIs())
|
.And(x => x.GivenTheDownstreamRouteIs())
|
||||||
.When(x => x.WhenICallTheMiddleware())
|
.When(x => x.WhenICallTheMiddleware())
|
||||||
.Then(x => x.ThenTheCacheAddIsCalledCorrectly())
|
.Then(x => x.ThenTheCacheAddIsCalledCorrectly())
|
||||||
@ -87,9 +103,9 @@
|
|||||||
.Returns(_response);
|
.Returns(_response);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GivenResponseIsNotCached()
|
private void GivenResponseIsNotCached(HttpResponseMessage responseMessage)
|
||||||
{
|
{
|
||||||
_downstreamContext.DownstreamResponse = new DownstreamResponse(new HttpResponseMessage());
|
_downstreamContext.DownstreamResponse = new DownstreamResponse(responseMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GivenTheDownstreamRouteIs()
|
private void GivenTheDownstreamRouteIs()
|
||||||
|
@ -199,7 +199,7 @@
|
|||||||
.WithDownstreamScheme("http")
|
.WithDownstreamScheme("http")
|
||||||
.WithUpstreamHttpMethod(new List<string>() {"Get"})
|
.WithUpstreamHttpMethod(new List<string>() {"Get"})
|
||||||
.WithDownstreamAddresses(new List<DownstreamHostAndPort>() {new DownstreamHostAndPort("localhost", 51878)})
|
.WithDownstreamAddresses(new List<DownstreamHostAndPort>() {new DownstreamHostAndPort("localhost", 51878)})
|
||||||
.WithLoadBalancerKey("/laura|Get")
|
.WithLoadBalancerKey("/laura|Get|localhost:51878")
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
var lauraReRoute = new ReRouteBuilder()
|
var lauraReRoute = new ReRouteBuilder()
|
||||||
@ -218,7 +218,7 @@
|
|||||||
.WithDownstreamScheme("http")
|
.WithDownstreamScheme("http")
|
||||||
.WithUpstreamHttpMethod(new List<string>() { "Get" })
|
.WithUpstreamHttpMethod(new List<string>() { "Get" })
|
||||||
.WithDownstreamAddresses(new List<DownstreamHostAndPort>() { new DownstreamHostAndPort("localhost", 51878) })
|
.WithDownstreamAddresses(new List<DownstreamHostAndPort>() { new DownstreamHostAndPort("localhost", 51878) })
|
||||||
.WithLoadBalancerKey("/tom|Get")
|
.WithLoadBalancerKey("/tom|Get|localhost:51880")
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
var tomReRoute = new ReRouteBuilder()
|
var tomReRoute = new ReRouteBuilder()
|
||||||
@ -409,7 +409,7 @@
|
|||||||
.WithDownstreamPathTemplate("/products/{productId}")
|
.WithDownstreamPathTemplate("/products/{productId}")
|
||||||
.WithUpstreamPathTemplate("/api/products/{productId}")
|
.WithUpstreamPathTemplate("/api/products/{productId}")
|
||||||
.WithUpstreamHttpMethod(new List<string> {"Get"})
|
.WithUpstreamHttpMethod(new List<string> {"Get"})
|
||||||
.WithLoadBalancerKey("/api/products/{productId}|Get")
|
.WithLoadBalancerKey("/api/products/{productId}|Get|127.0.0.1:0")
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
this.Given(x => x.GivenTheConfigIs(new FileConfiguration
|
this.Given(x => x.GivenTheConfigIs(new FileConfiguration
|
||||||
@ -461,7 +461,7 @@
|
|||||||
.WithUpstreamPathTemplate("/api/products/{productId}")
|
.WithUpstreamPathTemplate("/api/products/{productId}")
|
||||||
.WithUpstreamHttpMethod(new List<string> {"Get"})
|
.WithUpstreamHttpMethod(new List<string> {"Get"})
|
||||||
.WithDelegatingHandlers(handlers)
|
.WithDelegatingHandlers(handlers)
|
||||||
.WithLoadBalancerKey("/api/products/{productId}|Get")
|
.WithLoadBalancerKey("/api/products/{productId}|Get|")
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
this.Given(x => x.GivenTheConfigIs(new FileConfiguration
|
this.Given(x => x.GivenTheConfigIs(new FileConfiguration
|
||||||
@ -506,7 +506,7 @@
|
|||||||
.WithUpstreamHttpMethod(new List<string> {"Get"})
|
.WithUpstreamHttpMethod(new List<string> {"Get"})
|
||||||
.WithUseServiceDiscovery(true)
|
.WithUseServiceDiscovery(true)
|
||||||
.WithServiceName("ProductService")
|
.WithServiceName("ProductService")
|
||||||
.WithLoadBalancerKey("/api/products/{productId}|Get")
|
.WithLoadBalancerKey("/api/products/{productId}|Get|")
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
this.Given(x => x.GivenTheConfigIs(new FileConfiguration
|
this.Given(x => x.GivenTheConfigIs(new FileConfiguration
|
||||||
@ -557,7 +557,7 @@
|
|||||||
.WithUpstreamPathTemplate("/api/products/{productId}")
|
.WithUpstreamPathTemplate("/api/products/{productId}")
|
||||||
.WithUpstreamHttpMethod(new List<string> {"Get"})
|
.WithUpstreamHttpMethod(new List<string> {"Get"})
|
||||||
.WithUseServiceDiscovery(false)
|
.WithUseServiceDiscovery(false)
|
||||||
.WithLoadBalancerKey("/api/products/{productId}|Get")
|
.WithLoadBalancerKey("/api/products/{productId}|Get|")
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
this.Given(x => x.GivenTheConfigIs(new FileConfiguration
|
this.Given(x => x.GivenTheConfigIs(new FileConfiguration
|
||||||
@ -600,7 +600,7 @@
|
|||||||
.WithUpstreamPathTemplate("/api/products/{productId}")
|
.WithUpstreamPathTemplate("/api/products/{productId}")
|
||||||
.WithUpstreamHttpMethod(new List<string> {"Get"})
|
.WithUpstreamHttpMethod(new List<string> {"Get"})
|
||||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("(?i)/api/products/.*/$", 1))
|
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("(?i)/api/products/.*/$", 1))
|
||||||
.WithLoadBalancerKey("/api/products/{productId}|Get")
|
.WithLoadBalancerKey("/api/products/{productId}|Get|")
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
this.Given(x => x.GivenTheConfigIs(new FileConfiguration
|
this.Given(x => x.GivenTheConfigIs(new FileConfiguration
|
||||||
@ -645,7 +645,7 @@
|
|||||||
.WithUpstreamPathTemplate("/api/products/{productId}")
|
.WithUpstreamPathTemplate("/api/products/{productId}")
|
||||||
.WithUpstreamHttpMethod(new List<string> {"Get"})
|
.WithUpstreamHttpMethod(new List<string> {"Get"})
|
||||||
.WithRequestIdKey("blahhhh")
|
.WithRequestIdKey("blahhhh")
|
||||||
.WithLoadBalancerKey("/api/products/{productId}|Get")
|
.WithLoadBalancerKey("/api/products/{productId}|Get|")
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
this.Given(x => x.GivenTheConfigIs(new FileConfiguration
|
this.Given(x => x.GivenTheConfigIs(new FileConfiguration
|
||||||
@ -688,7 +688,7 @@
|
|||||||
{
|
{
|
||||||
var reRouteOptions = new ReRouteOptionsBuilder()
|
var reRouteOptions = new ReRouteOptionsBuilder()
|
||||||
.Build();
|
.Build();
|
||||||
var httpHandlerOptions = new HttpHandlerOptions(true, true,false);
|
var httpHandlerOptions = new HttpHandlerOptions(true, true,false, true);
|
||||||
|
|
||||||
this.Given(x => x.GivenTheConfigIs(new FileConfiguration
|
this.Given(x => x.GivenTheConfigIs(new FileConfiguration
|
||||||
{
|
{
|
||||||
@ -740,7 +740,7 @@
|
|||||||
{
|
{
|
||||||
new ClaimToThing("CustomerId", "CustomerId", "", 0),
|
new ClaimToThing("CustomerId", "CustomerId", "", 0),
|
||||||
})
|
})
|
||||||
.WithLoadBalancerKey("/api/products/{productId}|Get")
|
.WithLoadBalancerKey("/api/products/{productId}|Get|")
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
var expected = new List<ReRoute>
|
var expected = new List<ReRoute>
|
||||||
@ -783,7 +783,7 @@
|
|||||||
.WithUpstreamPathTemplate("/api/products/{productId}")
|
.WithUpstreamPathTemplate("/api/products/{productId}")
|
||||||
.WithUpstreamHttpMethod(new List<string> {"Get"})
|
.WithUpstreamHttpMethod(new List<string> {"Get"})
|
||||||
.WithAuthenticationOptions(authenticationOptions)
|
.WithAuthenticationOptions(authenticationOptions)
|
||||||
.WithLoadBalancerKey("/api/products/{productId}|Get")
|
.WithLoadBalancerKey("/api/products/{productId}|Get|")
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
var expected = new List<ReRoute>
|
var expected = new List<ReRoute>
|
||||||
|
@ -35,7 +35,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var expectedOptions = new HttpHandlerOptions(false, false, false);
|
var expectedOptions = new HttpHandlerOptions(false, false, false, true);
|
||||||
|
|
||||||
this.Given(x => GivenTheFollowing(fileReRoute))
|
this.Given(x => GivenTheFollowing(fileReRoute))
|
||||||
.When(x => WhenICreateHttpHandlerOptions())
|
.When(x => WhenICreateHttpHandlerOptions())
|
||||||
@ -54,7 +54,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var expectedOptions = new HttpHandlerOptions(false, false, true);
|
var expectedOptions = new HttpHandlerOptions(false, false, true, true);
|
||||||
|
|
||||||
this.Given(x => GivenTheFollowing(fileReRoute))
|
this.Given(x => GivenTheFollowing(fileReRoute))
|
||||||
.And(x => GivenARealTracer())
|
.And(x => GivenARealTracer())
|
||||||
@ -67,7 +67,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
public void should_create_options_with_useCookie_false_and_allowAutoRedirect_true_as_default()
|
public void should_create_options_with_useCookie_false_and_allowAutoRedirect_true_as_default()
|
||||||
{
|
{
|
||||||
var fileReRoute = new FileReRoute();
|
var fileReRoute = new FileReRoute();
|
||||||
var expectedOptions = new HttpHandlerOptions(false, false, false);
|
var expectedOptions = new HttpHandlerOptions(false, false, false, true);
|
||||||
|
|
||||||
this.Given(x => GivenTheFollowing(fileReRoute))
|
this.Given(x => GivenTheFollowing(fileReRoute))
|
||||||
.When(x => WhenICreateHttpHandlerOptions())
|
.When(x => WhenICreateHttpHandlerOptions())
|
||||||
@ -88,7 +88,42 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var expectedOptions = new HttpHandlerOptions(false, false, false);
|
var expectedOptions = new HttpHandlerOptions(false, false, false, true);
|
||||||
|
|
||||||
|
this.Given(x => GivenTheFollowing(fileReRoute))
|
||||||
|
.When(x => WhenICreateHttpHandlerOptions())
|
||||||
|
.Then(x => ThenTheFollowingOptionsReturned(expectedOptions))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_create_options_with_useproxy_true_as_default()
|
||||||
|
{
|
||||||
|
var fileReRoute = new FileReRoute
|
||||||
|
{
|
||||||
|
HttpHandlerOptions = new FileHttpHandlerOptions()
|
||||||
|
};
|
||||||
|
|
||||||
|
var expectedOptions = new HttpHandlerOptions(false, false, false, true);
|
||||||
|
|
||||||
|
this.Given(x => GivenTheFollowing(fileReRoute))
|
||||||
|
.When(x => WhenICreateHttpHandlerOptions())
|
||||||
|
.Then(x => ThenTheFollowingOptionsReturned(expectedOptions))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_create_options_with_specified_useproxy()
|
||||||
|
{
|
||||||
|
var fileReRoute = new FileReRoute
|
||||||
|
{
|
||||||
|
HttpHandlerOptions = new FileHttpHandlerOptions
|
||||||
|
{
|
||||||
|
UseProxy = false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var expectedOptions = new HttpHandlerOptions(false, false, false, false);
|
||||||
|
|
||||||
this.Given(x => GivenTheFollowing(fileReRoute))
|
this.Given(x => GivenTheFollowing(fileReRoute))
|
||||||
.When(x => WhenICreateHttpHandlerOptions())
|
.When(x => WhenICreateHttpHandlerOptions())
|
||||||
@ -112,6 +147,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
_httpHandlerOptions.AllowAutoRedirect.ShouldBe(expected.AllowAutoRedirect);
|
_httpHandlerOptions.AllowAutoRedirect.ShouldBe(expected.AllowAutoRedirect);
|
||||||
_httpHandlerOptions.UseCookieContainer.ShouldBe(expected.UseCookieContainer);
|
_httpHandlerOptions.UseCookieContainer.ShouldBe(expected.UseCookieContainer);
|
||||||
_httpHandlerOptions.UseTracing.ShouldBe(expected.UseTracing);
|
_httpHandlerOptions.UseTracing.ShouldBe(expected.UseTracing);
|
||||||
|
_httpHandlerOptions.UseProxy.ShouldBe(expected.UseProxy);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GivenARealTracer()
|
private void GivenARealTracer()
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
using System;
|
|
||||||
using Ocelot.Configuration.Creator;
|
using Ocelot.Configuration.Creator;
|
||||||
using Ocelot.Configuration.File;
|
using Ocelot.Configuration.File;
|
||||||
using Ocelot.Values;
|
using Ocelot.Values;
|
||||||
@ -30,7 +29,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
|
|
||||||
this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
|
this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
|
||||||
.When(x => x.WhenICreateTheTemplatePattern())
|
.When(x => x.WhenICreateTheTemplatePattern())
|
||||||
.Then(x => x.ThenTheFollowingIsReturned("^(?i)/orders/[0-9a-zA-Z].*$"))
|
.Then(x => x.ThenTheFollowingIsReturned("^(?i)/orders/.+$"))
|
||||||
.And(x => ThenThePriorityIs(0))
|
.And(x => ThenThePriorityIs(0))
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
@ -62,7 +61,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
|
|
||||||
this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
|
this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
|
||||||
.When(x => x.WhenICreateTheTemplatePattern())
|
.When(x => x.WhenICreateTheTemplatePattern())
|
||||||
.Then(x => x.ThenTheFollowingIsReturned("^(?i)/PRODUCTS/[0-9a-zA-Z].*$"))
|
.Then(x => x.ThenTheFollowingIsReturned("^(?i)/PRODUCTS/.+$"))
|
||||||
.And(x => ThenThePriorityIs(1))
|
.And(x => ThenThePriorityIs(1))
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
@ -93,7 +92,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
};
|
};
|
||||||
this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
|
this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
|
||||||
.When(x => x.WhenICreateTheTemplatePattern())
|
.When(x => x.WhenICreateTheTemplatePattern())
|
||||||
.Then(x => x.ThenTheFollowingIsReturned("^/PRODUCTS/[0-9a-zA-Z].*$"))
|
.Then(x => x.ThenTheFollowingIsReturned("^/PRODUCTS/.+$"))
|
||||||
.And(x => ThenThePriorityIs(1))
|
.And(x => ThenThePriorityIs(1))
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
@ -109,7 +108,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
|
|
||||||
this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
|
this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
|
||||||
.When(x => x.WhenICreateTheTemplatePattern())
|
.When(x => x.WhenICreateTheTemplatePattern())
|
||||||
.Then(x => x.ThenTheFollowingIsReturned("^/api/products/[0-9a-zA-Z].*$"))
|
.Then(x => x.ThenTheFollowingIsReturned("^/api/products/.+$"))
|
||||||
.And(x => ThenThePriorityIs(1))
|
.And(x => ThenThePriorityIs(1))
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
@ -125,7 +124,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
|
|
||||||
this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
|
this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
|
||||||
.When(x => x.WhenICreateTheTemplatePattern())
|
.When(x => x.WhenICreateTheTemplatePattern())
|
||||||
.Then(x => x.ThenTheFollowingIsReturned("^/api/products/[0-9a-zA-Z].*/variants/[0-9a-zA-Z].*$"))
|
.Then(x => x.ThenTheFollowingIsReturned("^/api/products/.+/variants/.+$"))
|
||||||
.And(x => ThenThePriorityIs(1))
|
.And(x => ThenThePriorityIs(1))
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
@ -141,7 +140,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
|
|
||||||
this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
|
this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
|
||||||
.When(x => x.WhenICreateTheTemplatePattern())
|
.When(x => x.WhenICreateTheTemplatePattern())
|
||||||
.Then(x => x.ThenTheFollowingIsReturned("^/api/products/[0-9a-zA-Z].*/variants/[0-9a-zA-Z].*(/|)$"))
|
.Then(x => x.ThenTheFollowingIsReturned("^/api/products/.+/variants/.+(/|)$"))
|
||||||
.And(x => ThenThePriorityIs(1))
|
.And(x => ThenThePriorityIs(1))
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
@ -187,7 +186,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
|
|
||||||
this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
|
this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
|
||||||
.When(x => x.WhenICreateTheTemplatePattern())
|
.When(x => x.WhenICreateTheTemplatePattern())
|
||||||
.Then(x => x.ThenTheFollowingIsReturned("^/[0-9a-zA-Z].*/products/variants/[0-9a-zA-Z].*(/|)$"))
|
.Then(x => x.ThenTheFollowingIsReturned("^/.+/products/variants/.+(/|)$"))
|
||||||
.And(x => ThenThePriorityIs(1))
|
.And(x => ThenThePriorityIs(1))
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ using Ocelot.Configuration;
|
|||||||
namespace Ocelot.UnitTests.Controllers
|
namespace Ocelot.UnitTests.Controllers
|
||||||
{
|
{
|
||||||
using Ocelot.Configuration.Repository;
|
using Ocelot.Configuration.Repository;
|
||||||
|
using Rafty.Concensus.Node;
|
||||||
|
|
||||||
public class FileConfigurationControllerTests
|
public class FileConfigurationControllerTests
|
||||||
{
|
{
|
||||||
@ -126,14 +127,14 @@ namespace Ocelot.UnitTests.Controllers
|
|||||||
{
|
{
|
||||||
_node
|
_node
|
||||||
.Setup(x => x.Accept(It.IsAny<UpdateFileConfiguration>()))
|
.Setup(x => x.Accept(It.IsAny<UpdateFileConfiguration>()))
|
||||||
.ReturnsAsync(new Rafty.Concensus.OkResponse<UpdateFileConfiguration>(new UpdateFileConfiguration(new FileConfiguration())));
|
.ReturnsAsync(new Rafty.Infrastructure.OkResponse<UpdateFileConfiguration>(new UpdateFileConfiguration(new FileConfiguration())));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GivenTheNodeReturnsError()
|
private void GivenTheNodeReturnsError()
|
||||||
{
|
{
|
||||||
_node
|
_node
|
||||||
.Setup(x => x.Accept(It.IsAny<UpdateFileConfiguration>()))
|
.Setup(x => x.Accept(It.IsAny<UpdateFileConfiguration>()))
|
||||||
.ReturnsAsync(new Rafty.Concensus.ErrorResponse<UpdateFileConfiguration>("error", new UpdateFileConfiguration(new FileConfiguration())));
|
.ReturnsAsync(new Rafty.Infrastructure.ErrorResponse<UpdateFileConfiguration>("error", new UpdateFileConfiguration(new FileConfiguration())));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GivenTheConfigSetterReturns(Response response)
|
private void GivenTheConfigSetterReturns(Response response)
|
||||||
|
@ -583,6 +583,19 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
|||||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1))
|
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1))
|
||||||
.WithUpstreamHost("MATCH")
|
.WithUpstreamHost("MATCH")
|
||||||
|
.Build(),
|
||||||
|
new ReRouteBuilder()
|
||||||
|
.WithDownstreamReRoute(new DownstreamReRouteBuilder()
|
||||||
|
.WithDownstreamPathTemplate("someDownstreamPath")
|
||||||
|
.WithUpstreamPathTemplate("someUpstreamPath")
|
||||||
|
.WithUpstreamHttpMethod(new List<string> { }) // empty list of methods
|
||||||
|
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1))
|
||||||
|
.WithUpstreamHost("MATCH")
|
||||||
|
.Build())
|
||||||
|
.WithUpstreamPathTemplate("someUpstreamPath")
|
||||||
|
.WithUpstreamHttpMethod(new List<string> { }) // empty list of methods
|
||||||
|
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1))
|
||||||
|
.WithUpstreamHost("MATCH")
|
||||||
.Build()
|
.Build()
|
||||||
}, string.Empty, serviceProviderConfig
|
}, string.Empty, serviceProviderConfig
|
||||||
))
|
))
|
||||||
@ -594,6 +607,71 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
|||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_not_return_route_when_host_doesnt_match_with_empty_upstream_http_method()
|
||||||
|
{
|
||||||
|
var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build();
|
||||||
|
|
||||||
|
this.Given(x => x.GivenThereIsAnUpstreamUrlPath("matchInUrlMatcher/"))
|
||||||
|
.And(x => GivenTheUpstreamHostIs("DONTMATCH"))
|
||||||
|
.And(x => x.GivenTheTemplateVariableAndNameFinderReturns(new OkResponse<List<PlaceholderNameAndValue>>(new List<PlaceholderNameAndValue>())))
|
||||||
|
.And(x => x.GivenTheConfigurationIs(new List<ReRoute>
|
||||||
|
{
|
||||||
|
new ReRouteBuilder()
|
||||||
|
.WithDownstreamReRoute(new DownstreamReRouteBuilder()
|
||||||
|
.WithDownstreamPathTemplate("someDownstreamPath")
|
||||||
|
.WithUpstreamPathTemplate("someUpstreamPath")
|
||||||
|
.WithUpstreamHttpMethod(new List<string>())
|
||||||
|
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1))
|
||||||
|
.WithUpstreamHost("MATCH")
|
||||||
|
.Build())
|
||||||
|
.WithUpstreamPathTemplate("someUpstreamPath")
|
||||||
|
.WithUpstreamHttpMethod(new List<string>())
|
||||||
|
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1))
|
||||||
|
.WithUpstreamHost("MATCH")
|
||||||
|
.Build()
|
||||||
|
}, string.Empty, serviceProviderConfig
|
||||||
|
))
|
||||||
|
.And(x => x.GivenTheUrlMatcherReturns(new OkResponse<UrlMatch>(new UrlMatch(true))))
|
||||||
|
.And(x => x.GivenTheUpstreamHttpMethodIs("Get"))
|
||||||
|
.When(x => x.WhenICallTheFinder())
|
||||||
|
.Then(x => x.ThenAnErrorResponseIsReturned())
|
||||||
|
.And(x => x.ThenTheUrlMatcherIsNotCalled())
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_route_when_host_does_match_with_empty_upstream_http_method()
|
||||||
|
{
|
||||||
|
var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build();
|
||||||
|
|
||||||
|
this.Given(x => x.GivenThereIsAnUpstreamUrlPath("matchInUrlMatcher/"))
|
||||||
|
.And(x => GivenTheUpstreamHostIs("MATCH"))
|
||||||
|
.And(x => x.GivenTheTemplateVariableAndNameFinderReturns(new OkResponse<List<PlaceholderNameAndValue>>(new List<PlaceholderNameAndValue>())))
|
||||||
|
.And(x => x.GivenTheConfigurationIs(new List<ReRoute>
|
||||||
|
{
|
||||||
|
new ReRouteBuilder()
|
||||||
|
.WithDownstreamReRoute(new DownstreamReRouteBuilder()
|
||||||
|
.WithDownstreamPathTemplate("someDownstreamPath")
|
||||||
|
.WithUpstreamPathTemplate("someUpstreamPath")
|
||||||
|
.WithUpstreamHttpMethod(new List<string>())
|
||||||
|
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1))
|
||||||
|
.WithUpstreamHost("MATCH")
|
||||||
|
.Build())
|
||||||
|
.WithUpstreamPathTemplate("someUpstreamPath")
|
||||||
|
.WithUpstreamHttpMethod(new List<string>())
|
||||||
|
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1))
|
||||||
|
.WithUpstreamHost("MATCH")
|
||||||
|
.Build()
|
||||||
|
}, string.Empty, serviceProviderConfig
|
||||||
|
))
|
||||||
|
.And(x => x.GivenTheUrlMatcherReturns(new OkResponse<UrlMatch>(new UrlMatch(true))))
|
||||||
|
.And(x => x.GivenTheUpstreamHttpMethodIs("Get"))
|
||||||
|
.When(x => x.WhenICallTheFinder())
|
||||||
|
.And(x => x.ThenTheUrlMatcherIsCalledCorrectly(1))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void should_return_route_when_host_matches_but_null_host_on_same_path_first()
|
public void should_return_route_when_host_matches_but_null_host_on_same_path_first()
|
||||||
{
|
{
|
||||||
|
@ -26,7 +26,7 @@ namespace Ocelot.UnitTests.LoadBalancer
|
|||||||
{
|
{
|
||||||
_factory = new Mock<ILoadBalancerFactory>();
|
_factory = new Mock<ILoadBalancerFactory>();
|
||||||
_loadBalancerHouse = new LoadBalancerHouse(_factory.Object);
|
_loadBalancerHouse = new LoadBalancerHouse(_factory.Object);
|
||||||
_serviceProviderConfig = new ServiceProviderConfiguration("myType","myHost",123, string.Empty, "configKey");
|
_serviceProviderConfig = new ServiceProviderConfiguration("myType","myHost",123, string.Empty, "configKey", 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
@ -7,6 +7,8 @@ namespace Ocelot.UnitTests.Middleware
|
|||||||
using Ocelot.Middleware.Pipeline;
|
using Ocelot.Middleware.Pipeline;
|
||||||
using Pivotal.Discovery.Client;
|
using Pivotal.Discovery.Client;
|
||||||
using Shouldly;
|
using Shouldly;
|
||||||
|
using Steeltoe.Common.Discovery;
|
||||||
|
using Steeltoe.Discovery.Eureka;
|
||||||
using TestStack.BDDfy;
|
using TestStack.BDDfy;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
@ -40,7 +42,16 @@ namespace Ocelot.UnitTests.Middleware
|
|||||||
var root = test.Build();
|
var root = test.Build();
|
||||||
var services = new ServiceCollection();
|
var services = new ServiceCollection();
|
||||||
services.AddSingleton<IConfiguration>(root);
|
services.AddSingleton<IConfiguration>(root);
|
||||||
services.AddDiscoveryClient(new DiscoveryOptions {ClientType = DiscoveryClientType.EUREKA});
|
services.AddDiscoveryClient(new DiscoveryOptions
|
||||||
|
{
|
||||||
|
ClientType = DiscoveryClientType.EUREKA,
|
||||||
|
//options can not be null
|
||||||
|
ClientOptions = new EurekaClientOptions()
|
||||||
|
{
|
||||||
|
ShouldFetchRegistry = false,
|
||||||
|
ShouldRegisterWithEureka = false
|
||||||
|
}
|
||||||
|
});
|
||||||
services.AddOcelot();
|
services.AddOcelot();
|
||||||
var provider = services.BuildServiceProvider();
|
var provider = services.BuildServiceProvider();
|
||||||
_builder = new OcelotPipelineBuilder(provider);
|
_builder = new OcelotPipelineBuilder(provider);
|
||||||
|
@ -28,6 +28,15 @@
|
|||||||
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
|
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Update="appsettings.json">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="idsrv3test.pfx">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.8" />
|
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.8" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="2.0.3" />
|
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="2.0.3" />
|
||||||
@ -49,7 +58,7 @@
|
|||||||
<PackageReference Include="TestStack.BDDfy" Version="4.3.2" />
|
<PackageReference Include="TestStack.BDDfy" Version="4.3.2" />
|
||||||
<PackageReference Include="xunit" Version="2.3.1" />
|
<PackageReference Include="xunit" Version="2.3.1" />
|
||||||
<PackageReference Include="Butterfly.Client.AspNetCore" Version="0.0.8" />
|
<PackageReference Include="Butterfly.Client.AspNetCore" Version="0.0.8" />
|
||||||
<PackageReference Include="Rafty" Version="0.4.3" />
|
<PackageReference Include="Rafty" Version="0.4.4" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -0,0 +1,93 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Moq;
|
||||||
|
using Ocelot.Infrastructure;
|
||||||
|
using Ocelot.Request.Creator;
|
||||||
|
using Ocelot.Request.Middleware;
|
||||||
|
using Shouldly;
|
||||||
|
using TestStack.BDDfy;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Ocelot.UnitTests.Request.Creator
|
||||||
|
{
|
||||||
|
public class DownstreamRequestCreatorTests
|
||||||
|
{
|
||||||
|
private Mock<IFrameworkDescription> _framework;
|
||||||
|
private DownstreamRequestCreator _downstreamRequestCreator;
|
||||||
|
private HttpRequestMessage _request;
|
||||||
|
private DownstreamRequest _result;
|
||||||
|
|
||||||
|
public DownstreamRequestCreatorTests()
|
||||||
|
{
|
||||||
|
_framework = new Mock<IFrameworkDescription>();
|
||||||
|
_downstreamRequestCreator = new DownstreamRequestCreator(_framework.Object);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_create_downstream_request()
|
||||||
|
{
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Get, "http://www.test.com");
|
||||||
|
var content = new StringContent("test");
|
||||||
|
request.Content = content;
|
||||||
|
|
||||||
|
this.Given(_ => GivenTheFrameworkIs(""))
|
||||||
|
.And(_ => GivenTheRequestIs(request))
|
||||||
|
.When(_ => WhenICreate())
|
||||||
|
.Then(_ => ThenTheDownstreamRequestHasABody())
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_remove_body_for_http_methods()
|
||||||
|
{
|
||||||
|
var methods = new List<HttpMethod> { HttpMethod.Get, HttpMethod.Head, HttpMethod.Delete, HttpMethod.Trace };
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Get, "http://www.test.com");
|
||||||
|
var content = new StringContent("test");
|
||||||
|
request.Content = content;
|
||||||
|
|
||||||
|
methods.ForEach(m => {
|
||||||
|
this.Given(_ => GivenTheFrameworkIs(".NET Framework"))
|
||||||
|
.And(_ => GivenTheRequestIs(request))
|
||||||
|
.When(_ => WhenICreate())
|
||||||
|
.Then(_ => ThenTheDownstreamRequestDoesNotHaveABody())
|
||||||
|
.BDDfy();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenTheFrameworkIs(string framework)
|
||||||
|
{
|
||||||
|
_framework.Setup(x => x.Get()).Returns(framework);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenTheRequestIs(HttpRequestMessage request)
|
||||||
|
{
|
||||||
|
_request = request;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WhenICreate()
|
||||||
|
{
|
||||||
|
_result = _downstreamRequestCreator.Create(_request);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ThenTheDownstreamRequestHasABody()
|
||||||
|
{
|
||||||
|
_result.ShouldNotBeNull();
|
||||||
|
_result.Method.ToLower().ShouldBe("get");
|
||||||
|
_result.Scheme.ToLower().ShouldBe("http");
|
||||||
|
_result.Host.ToLower().ShouldBe("www.test.com");
|
||||||
|
var resultContent = await _result.ToHttpRequestMessage().Content.ReadAsStringAsync();
|
||||||
|
resultContent.ShouldBe("test");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThenTheDownstreamRequestDoesNotHaveABody()
|
||||||
|
{
|
||||||
|
_result.ShouldNotBeNull();
|
||||||
|
_result.Method.ToLower().ShouldBe("get");
|
||||||
|
_result.Scheme.ToLower().ShouldBe("http");
|
||||||
|
_result.Host.ToLower().ShouldBe("www.test.com");
|
||||||
|
_result.ToHttpRequestMessage().Content.ShouldBeNull();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -14,6 +14,8 @@ namespace Ocelot.UnitTests.Request
|
|||||||
using Ocelot.Responses;
|
using Ocelot.Responses;
|
||||||
using Ocelot.DownstreamRouteFinder.Middleware;
|
using Ocelot.DownstreamRouteFinder.Middleware;
|
||||||
using Shouldly;
|
using Shouldly;
|
||||||
|
using Ocelot.Request.Creator;
|
||||||
|
using Ocelot.Infrastructure;
|
||||||
|
|
||||||
public class DownstreamRequestInitialiserMiddlewareTests
|
public class DownstreamRequestInitialiserMiddlewareTests
|
||||||
{
|
{
|
||||||
@ -50,7 +52,8 @@ namespace Ocelot.UnitTests.Request
|
|||||||
_middleware = new DownstreamRequestInitialiserMiddleware(
|
_middleware = new DownstreamRequestInitialiserMiddleware(
|
||||||
_next.Object,
|
_next.Object,
|
||||||
_loggerFactory.Object,
|
_loggerFactory.Object,
|
||||||
_requestMapper.Object);
|
_requestMapper.Object,
|
||||||
|
new DownstreamRequestCreator(new FrameworkDescription()));
|
||||||
|
|
||||||
_downstreamContext = new DownstreamContext(_httpContext.Object);
|
_downstreamContext = new DownstreamContext(_httpContext.Object);
|
||||||
}
|
}
|
||||||
|
@ -46,7 +46,7 @@ namespace Ocelot.UnitTests.Requester
|
|||||||
|
|
||||||
var reRoute = new DownstreamReRouteBuilder()
|
var reRoute = new DownstreamReRouteBuilder()
|
||||||
.WithQosOptions(qosOptions)
|
.WithQosOptions(qosOptions)
|
||||||
.WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true))
|
.WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true, true))
|
||||||
.WithDelegatingHandlers(new List<string>
|
.WithDelegatingHandlers(new List<string>
|
||||||
{
|
{
|
||||||
"FakeDelegatingHandler",
|
"FakeDelegatingHandler",
|
||||||
@ -82,7 +82,7 @@ namespace Ocelot.UnitTests.Requester
|
|||||||
|
|
||||||
var reRoute = new DownstreamReRouteBuilder()
|
var reRoute = new DownstreamReRouteBuilder()
|
||||||
.WithQosOptions(qosOptions)
|
.WithQosOptions(qosOptions)
|
||||||
.WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true))
|
.WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true, true))
|
||||||
.WithDelegatingHandlers(new List<string>
|
.WithDelegatingHandlers(new List<string>
|
||||||
{
|
{
|
||||||
"FakeDelegatingHandlerTwo",
|
"FakeDelegatingHandlerTwo",
|
||||||
@ -119,7 +119,7 @@ namespace Ocelot.UnitTests.Requester
|
|||||||
|
|
||||||
var reRoute = new DownstreamReRouteBuilder()
|
var reRoute = new DownstreamReRouteBuilder()
|
||||||
.WithQosOptions(qosOptions)
|
.WithQosOptions(qosOptions)
|
||||||
.WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true))
|
.WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true, true))
|
||||||
.WithDelegatingHandlers(new List<string>
|
.WithDelegatingHandlers(new List<string>
|
||||||
{
|
{
|
||||||
"FakeDelegatingHandlerTwo",
|
"FakeDelegatingHandlerTwo",
|
||||||
@ -155,7 +155,7 @@ namespace Ocelot.UnitTests.Requester
|
|||||||
|
|
||||||
var reRoute = new DownstreamReRouteBuilder()
|
var reRoute = new DownstreamReRouteBuilder()
|
||||||
.WithQosOptions(qosOptions)
|
.WithQosOptions(qosOptions)
|
||||||
.WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true))
|
.WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true, true))
|
||||||
.WithDelegatingHandlers(new List<string>
|
.WithDelegatingHandlers(new List<string>
|
||||||
{
|
{
|
||||||
"FakeDelegatingHandler",
|
"FakeDelegatingHandler",
|
||||||
@ -189,7 +189,7 @@ namespace Ocelot.UnitTests.Requester
|
|||||||
|
|
||||||
var reRoute = new DownstreamReRouteBuilder()
|
var reRoute = new DownstreamReRouteBuilder()
|
||||||
.WithQosOptions(qosOptions)
|
.WithQosOptions(qosOptions)
|
||||||
.WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true))
|
.WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true, true))
|
||||||
.WithLoadBalancerKey("")
|
.WithLoadBalancerKey("")
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
@ -215,7 +215,7 @@ namespace Ocelot.UnitTests.Requester
|
|||||||
|
|
||||||
var reRoute = new DownstreamReRouteBuilder()
|
var reRoute = new DownstreamReRouteBuilder()
|
||||||
.WithQosOptions(qosOptions)
|
.WithQosOptions(qosOptions)
|
||||||
.WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false))
|
.WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false, true))
|
||||||
.WithDelegatingHandlers(new List<string>
|
.WithDelegatingHandlers(new List<string>
|
||||||
{
|
{
|
||||||
"FakeDelegatingHandler",
|
"FakeDelegatingHandler",
|
||||||
@ -244,7 +244,7 @@ namespace Ocelot.UnitTests.Requester
|
|||||||
|
|
||||||
var reRoute = new DownstreamReRouteBuilder()
|
var reRoute = new DownstreamReRouteBuilder()
|
||||||
.WithQosOptions(qosOptions)
|
.WithQosOptions(qosOptions)
|
||||||
.WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false)).WithLoadBalancerKey("").Build();
|
.WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false, true)).WithLoadBalancerKey("").Build();
|
||||||
|
|
||||||
this.Given(x => GivenTheFollowingRequest(reRoute))
|
this.Given(x => GivenTheFollowingRequest(reRoute))
|
||||||
.And(x => GivenTheQosProviderHouseReturns(new OkResponse<IQoSProvider>(It.IsAny<PollyQoSProvider>())))
|
.And(x => GivenTheQosProviderHouseReturns(new OkResponse<IQoSProvider>(It.IsAny<PollyQoSProvider>())))
|
||||||
@ -264,7 +264,7 @@ namespace Ocelot.UnitTests.Requester
|
|||||||
|
|
||||||
var reRoute = new DownstreamReRouteBuilder()
|
var reRoute = new DownstreamReRouteBuilder()
|
||||||
.WithQosOptions(qosOptions)
|
.WithQosOptions(qosOptions)
|
||||||
.WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false)).WithLoadBalancerKey("").Build();
|
.WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false, true)).WithLoadBalancerKey("").Build();
|
||||||
|
|
||||||
this.Given(x => GivenTheFollowingRequest(reRoute))
|
this.Given(x => GivenTheFollowingRequest(reRoute))
|
||||||
.And(x => GivenTheServiceProviderReturnsNothing())
|
.And(x => GivenTheServiceProviderReturnsNothing())
|
||||||
@ -284,7 +284,7 @@ namespace Ocelot.UnitTests.Requester
|
|||||||
|
|
||||||
var reRoute = new DownstreamReRouteBuilder()
|
var reRoute = new DownstreamReRouteBuilder()
|
||||||
.WithQosOptions(qosOptions)
|
.WithQosOptions(qosOptions)
|
||||||
.WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false)).WithLoadBalancerKey("").Build();
|
.WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false, true)).WithLoadBalancerKey("").Build();
|
||||||
|
|
||||||
this.Given(x => GivenTheFollowingRequest(reRoute))
|
this.Given(x => GivenTheFollowingRequest(reRoute))
|
||||||
.And(x => GivenTheQosProviderHouseReturns(new OkResponse<IQoSProvider>(It.IsAny<PollyQoSProvider>())))
|
.And(x => GivenTheQosProviderHouseReturns(new OkResponse<IQoSProvider>(It.IsAny<PollyQoSProvider>())))
|
||||||
@ -306,7 +306,7 @@ namespace Ocelot.UnitTests.Requester
|
|||||||
|
|
||||||
var reRoute = new DownstreamReRouteBuilder()
|
var reRoute = new DownstreamReRouteBuilder()
|
||||||
.WithQosOptions(qosOptions)
|
.WithQosOptions(qosOptions)
|
||||||
.WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false)).WithLoadBalancerKey("").Build();
|
.WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false, true)).WithLoadBalancerKey("").Build();
|
||||||
|
|
||||||
this.Given(x => GivenTheFollowingRequest(reRoute))
|
this.Given(x => GivenTheFollowingRequest(reRoute))
|
||||||
.And(x => GivenTheQosProviderHouseReturns(new ErrorResponse<IQoSProvider>(It.IsAny<Error>())))
|
.And(x => GivenTheQosProviderHouseReturns(new ErrorResponse<IQoSProvider>(It.IsAny<Error>())))
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
@ -51,7 +49,7 @@ namespace Ocelot.UnitTests.Requester
|
|||||||
|
|
||||||
var reRoute = new DownstreamReRouteBuilder()
|
var reRoute = new DownstreamReRouteBuilder()
|
||||||
.WithQosOptions(qosOptions)
|
.WithQosOptions(qosOptions)
|
||||||
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false))
|
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true))
|
||||||
.WithLoadBalancerKey("")
|
.WithLoadBalancerKey("")
|
||||||
.WithQosOptions(new QoSOptionsBuilder().Build())
|
.WithQosOptions(new QoSOptionsBuilder().Build())
|
||||||
.Build();
|
.Build();
|
||||||
@ -71,7 +69,7 @@ namespace Ocelot.UnitTests.Requester
|
|||||||
|
|
||||||
var reRoute = new DownstreamReRouteBuilder()
|
var reRoute = new DownstreamReRouteBuilder()
|
||||||
.WithQosOptions(qosOptions)
|
.WithQosOptions(qosOptions)
|
||||||
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false))
|
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true))
|
||||||
.WithLoadBalancerKey("")
|
.WithLoadBalancerKey("")
|
||||||
.WithQosOptions(new QoSOptionsBuilder().Build())
|
.WithQosOptions(new QoSOptionsBuilder().Build())
|
||||||
.WithDangerousAcceptAnyServerCertificateValidator(true)
|
.WithDangerousAcceptAnyServerCertificateValidator(true)
|
||||||
@ -93,7 +91,7 @@ namespace Ocelot.UnitTests.Requester
|
|||||||
|
|
||||||
var reRoute = new DownstreamReRouteBuilder()
|
var reRoute = new DownstreamReRouteBuilder()
|
||||||
.WithQosOptions(qosOptions)
|
.WithQosOptions(qosOptions)
|
||||||
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false))
|
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true))
|
||||||
.WithLoadBalancerKey("")
|
.WithLoadBalancerKey("")
|
||||||
.WithQosOptions(new QoSOptionsBuilder().Build())
|
.WithQosOptions(new QoSOptionsBuilder().Build())
|
||||||
.Build();
|
.Build();
|
||||||
@ -124,7 +122,7 @@ namespace Ocelot.UnitTests.Requester
|
|||||||
|
|
||||||
var reRoute = new DownstreamReRouteBuilder()
|
var reRoute = new DownstreamReRouteBuilder()
|
||||||
.WithQosOptions(qosOptions)
|
.WithQosOptions(qosOptions)
|
||||||
.WithHttpHandlerOptions(new HttpHandlerOptions(false, true, false))
|
.WithHttpHandlerOptions(new HttpHandlerOptions(false, true, false, true))
|
||||||
.WithLoadBalancerKey("")
|
.WithLoadBalancerKey("")
|
||||||
.WithQosOptions(new QoSOptionsBuilder().Build())
|
.WithQosOptions(new QoSOptionsBuilder().Build())
|
||||||
.Build();
|
.Build();
|
||||||
@ -159,7 +157,7 @@ namespace Ocelot.UnitTests.Requester
|
|||||||
|
|
||||||
var reRoute = new DownstreamReRouteBuilder()
|
var reRoute = new DownstreamReRouteBuilder()
|
||||||
.WithQosOptions(qosOptions)
|
.WithQosOptions(qosOptions)
|
||||||
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false))
|
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true))
|
||||||
.WithLoadBalancerKey("")
|
.WithLoadBalancerKey("")
|
||||||
.WithQosOptions(new QoSOptionsBuilder().Build())
|
.WithQosOptions(new QoSOptionsBuilder().Build())
|
||||||
.Build();
|
.Build();
|
||||||
|
@ -52,7 +52,7 @@ namespace Ocelot.UnitTests.Requester
|
|||||||
|
|
||||||
var reRoute = new DownstreamReRouteBuilder()
|
var reRoute = new DownstreamReRouteBuilder()
|
||||||
.WithQosOptions(qosOptions)
|
.WithQosOptions(qosOptions)
|
||||||
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false))
|
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true))
|
||||||
.WithLoadBalancerKey("")
|
.WithLoadBalancerKey("")
|
||||||
.WithQosOptions(new QoSOptionsBuilder().Build())
|
.WithQosOptions(new QoSOptionsBuilder().Build())
|
||||||
.Build();
|
.Build();
|
||||||
@ -78,7 +78,7 @@ namespace Ocelot.UnitTests.Requester
|
|||||||
|
|
||||||
var reRoute = new DownstreamReRouteBuilder()
|
var reRoute = new DownstreamReRouteBuilder()
|
||||||
.WithQosOptions(qosOptions)
|
.WithQosOptions(qosOptions)
|
||||||
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false))
|
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true))
|
||||||
.WithLoadBalancerKey("")
|
.WithLoadBalancerKey("")
|
||||||
.WithQosOptions(new QoSOptionsBuilder().Build())
|
.WithQosOptions(new QoSOptionsBuilder().Build())
|
||||||
.Build();
|
.Build();
|
||||||
@ -103,7 +103,7 @@ namespace Ocelot.UnitTests.Requester
|
|||||||
|
|
||||||
var reRoute = new DownstreamReRouteBuilder()
|
var reRoute = new DownstreamReRouteBuilder()
|
||||||
.WithQosOptions(qosOptions)
|
.WithQosOptions(qosOptions)
|
||||||
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false))
|
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true))
|
||||||
.WithLoadBalancerKey("")
|
.WithLoadBalancerKey("")
|
||||||
.WithQosOptions(new QoSOptionsBuilder().WithTimeoutValue(1).Build())
|
.WithQosOptions(new QoSOptionsBuilder().WithTimeoutValue(1).Build())
|
||||||
.Build();
|
.Build();
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
using Ocelot.ServiceDiscovery.Providers;
|
using Ocelot.ServiceDiscovery.Providers;
|
||||||
using Pivotal.Discovery.Client;
|
using Pivotal.Discovery.Client;
|
||||||
using Shouldly;
|
using Shouldly;
|
||||||
|
using Steeltoe.Common.Discovery;
|
||||||
using TestStack.BDDfy;
|
using TestStack.BDDfy;
|
||||||
using Values;
|
using Values;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
@ -0,0 +1,89 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using Consul;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Moq;
|
||||||
|
using Ocelot.Infrastructure.Consul;
|
||||||
|
using Ocelot.Logging;
|
||||||
|
using Ocelot.ServiceDiscovery.Configuration;
|
||||||
|
using Ocelot.ServiceDiscovery.Providers;
|
||||||
|
using Ocelot.Values;
|
||||||
|
using Xunit;
|
||||||
|
using TestStack.BDDfy;
|
||||||
|
using Shouldly;
|
||||||
|
using static Ocelot.Infrastructure.Wait;
|
||||||
|
|
||||||
|
namespace Ocelot.UnitTests.ServiceDiscovery
|
||||||
|
{
|
||||||
|
public class PollingConsulServiceDiscoveryProviderTests
|
||||||
|
{
|
||||||
|
private readonly int _delay;
|
||||||
|
private PollingConsulServiceDiscoveryProvider _provider;
|
||||||
|
private readonly string _serviceName;
|
||||||
|
private List<Service> _services;
|
||||||
|
private readonly Mock<IOcelotLoggerFactory> _factory;
|
||||||
|
private readonly Mock<IOcelotLogger> _logger;
|
||||||
|
private Mock<IServiceDiscoveryProvider> _consulServiceDiscoveryProvider;
|
||||||
|
private List<Service> _result;
|
||||||
|
|
||||||
|
public PollingConsulServiceDiscoveryProviderTests()
|
||||||
|
{
|
||||||
|
_services = new List<Service>();
|
||||||
|
_delay = 1;
|
||||||
|
_factory = new Mock<IOcelotLoggerFactory>();
|
||||||
|
_logger = new Mock<IOcelotLogger>();
|
||||||
|
_factory.Setup(x => x.CreateLogger<PollingConsulServiceDiscoveryProvider>()).Returns(_logger.Object);
|
||||||
|
_consulServiceDiscoveryProvider = new Mock<IServiceDiscoveryProvider>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_service_from_consul()
|
||||||
|
{
|
||||||
|
var service = new Service("", new ServiceHostAndPort("", 0), "", "", new List<string>());
|
||||||
|
|
||||||
|
this.Given(x => GivenConsulReturns(service))
|
||||||
|
.When(x => WhenIGetTheServices(1))
|
||||||
|
.Then(x => ThenTheCountIs(1))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenConsulReturns(Service service)
|
||||||
|
{
|
||||||
|
_services.Add(service);
|
||||||
|
_consulServiceDiscoveryProvider.Setup(x => x.Get()).ReturnsAsync(_services);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThenTheCountIs(int count)
|
||||||
|
{
|
||||||
|
_result.Count.ShouldBe(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WhenIGetTheServices(int expected)
|
||||||
|
{
|
||||||
|
_provider = new PollingConsulServiceDiscoveryProvider(_delay, _serviceName, _factory.Object, _consulServiceDiscoveryProvider.Object);
|
||||||
|
|
||||||
|
var result = WaitFor(3000).Until(() => {
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_result = _provider.Get().GetAwaiter().GetResult();
|
||||||
|
if(_result.Count == expected)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
catch(Exception)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
result.ShouldBeTrue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -14,6 +14,7 @@ using Xunit;
|
|||||||
namespace Ocelot.UnitTests.ServiceDiscovery
|
namespace Ocelot.UnitTests.ServiceDiscovery
|
||||||
{
|
{
|
||||||
using Pivotal.Discovery.Client;
|
using Pivotal.Discovery.Client;
|
||||||
|
using Steeltoe.Common.Discovery;
|
||||||
|
|
||||||
public class ServiceProviderFactoryTests
|
public class ServiceProviderFactoryTests
|
||||||
{
|
{
|
||||||
@ -23,12 +24,16 @@ namespace Ocelot.UnitTests.ServiceDiscovery
|
|||||||
private DownstreamReRoute _reRoute;
|
private DownstreamReRoute _reRoute;
|
||||||
private Mock<IOcelotLoggerFactory> _loggerFactory;
|
private Mock<IOcelotLoggerFactory> _loggerFactory;
|
||||||
private Mock<IDiscoveryClient> _discoveryClient;
|
private Mock<IDiscoveryClient> _discoveryClient;
|
||||||
|
private Mock<IOcelotLogger> _logger;
|
||||||
|
|
||||||
public ServiceProviderFactoryTests()
|
public ServiceProviderFactoryTests()
|
||||||
{
|
{
|
||||||
_loggerFactory = new Mock<IOcelotLoggerFactory>();
|
_loggerFactory = new Mock<IOcelotLoggerFactory>();
|
||||||
|
_logger = new Mock<IOcelotLogger>();
|
||||||
|
_loggerFactory.Setup(x => x.CreateLogger<PollingConsulServiceDiscoveryProvider>()).Returns(_logger.Object);
|
||||||
_discoveryClient = new Mock<IDiscoveryClient>();
|
_discoveryClient = new Mock<IDiscoveryClient>();
|
||||||
_factory = new ServiceDiscoveryProviderFactory(_loggerFactory.Object, new ConsulClientFactory(), _discoveryClient.Object);
|
var consulClient = new Mock<IConsulClientFactory>();
|
||||||
|
_factory = new ServiceDiscoveryProviderFactory(_loggerFactory.Object, consulClient.Object, _discoveryClient.Object);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@ -83,6 +88,25 @@ namespace Ocelot.UnitTests.ServiceDiscovery
|
|||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_polling_consul_service_provider()
|
||||||
|
{
|
||||||
|
var reRoute = new DownstreamReRouteBuilder()
|
||||||
|
.WithServiceName("product")
|
||||||
|
.WithUseServiceDiscovery(true)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var serviceConfig = new ServiceProviderConfigurationBuilder()
|
||||||
|
.WithType("PollConsul")
|
||||||
|
.WithPollingInterval(100000)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
this.Given(x => x.GivenTheReRoute(serviceConfig, reRoute))
|
||||||
|
.When(x => x.WhenIGetTheServiceProvider())
|
||||||
|
.Then(x => x.ThenTheServiceProviderIs<PollingConsulServiceDiscoveryProvider>())
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void should_return_service_fabric_provider()
|
public void should_return_service_fabric_provider()
|
||||||
{
|
{
|
||||||
|
BIN
test/Ocelot.UnitTests/idsrv3test.pfx
Normal file
BIN
test/Ocelot.UnitTests/idsrv3test.pfx
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user