mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-04-22 06:22:50 +08:00
Merge remote-tracking branch 'refs/remotes/origin/develop' into RateLimit
# Conflicts: # src/Ocelot/Configuration/Builder/ReRouteBuilder.cs # src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs # src/Ocelot/Configuration/QoSOptions.cs # src/Ocelot/Configuration/ReRoute.cs # test/Ocelot.AcceptanceTests/configuration.json
This commit is contained in:
commit
0aad1f8fa0
6
.gitignore
vendored
6
.gitignore
vendored
@ -236,3 +236,9 @@ _Pvt_Extensions
|
|||||||
# FAKE - F# Make
|
# FAKE - F# Make
|
||||||
.fake/
|
.fake/
|
||||||
tools/
|
tools/
|
||||||
|
|
||||||
|
# MacOS
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Ocelot acceptance test config
|
||||||
|
test/Ocelot.AcceptanceTests/configuration.json
|
50
README.md
50
README.md
@ -122,7 +122,7 @@ Ocelot's primary functionality is to take incomeing http requests and forward th
|
|||||||
to a downstream service. At the moment in the form of another http request (in the future
|
to a downstream service. At the moment in the form of another http request (in the future
|
||||||
this could be any transport mechanism.).
|
this could be any transport mechanism.).
|
||||||
|
|
||||||
Ocelot always adds a trailing slash to an UpstreamTemplate.
|
Ocelot always adds a trailing slash to an UpstreamPathTemplate.
|
||||||
|
|
||||||
Ocelot's describes the routing of one request to another as a ReRoute. In order to get
|
Ocelot's describes the routing of one request to another as a ReRoute. In order to get
|
||||||
anything working in Ocelot you need to set up a ReRoute in the configuration.
|
anything working in Ocelot you need to set up a ReRoute in the configuration.
|
||||||
@ -140,16 +140,16 @@ the following.
|
|||||||
"DownstreamScheme": "https",
|
"DownstreamScheme": "https",
|
||||||
"DownstreamPort": 80,
|
"DownstreamPort": 80,
|
||||||
"DownstreamHost" "localhost"
|
"DownstreamHost" "localhost"
|
||||||
"UpstreamTemplate": "/posts/{postId}",
|
"UpstreamPathTemplate": "/posts/{postId}",
|
||||||
"UpstreamHttpMethod": "Put"
|
"UpstreamHttpMethod": "Put"
|
||||||
}
|
}
|
||||||
|
|
||||||
The DownstreamPathTemplate,Scheme, Port and Host make the URL that this request will be forwarded to.
|
The DownstreamPathTemplate,Scheme, Port and Host make the URL that this request will be forwarded to.
|
||||||
The UpstreamTemplate is the URL that Ocelot will use to identity which
|
The UpstreamPathTemplate is the URL that Ocelot will use to identity which
|
||||||
DownstreamPathTemplate to use for a given request. Finally the UpstreamHttpMethod is used so
|
DownstreamPathTemplate to use for a given request. Finally the UpstreamHttpMethod is used so
|
||||||
Ocelot can distinguish between requests to the same URL and is obviously needed to work :)
|
Ocelot can distinguish between requests to the same URL and is obviously needed to work :)
|
||||||
In Ocelot you can add placeholders for variables to your Templates in the form of {something}.
|
In Ocelot you can add placeholders for variables to your Templates in the form of {something}.
|
||||||
The placeholder needs to be in both the DownstreamPathTemplate and UpstreamTemplate. If it is
|
The placeholder needs to be in both the DownstreamPathTemplate and UpstreamPathTemplate. If it is
|
||||||
Ocelot will attempt to replace the placeholder with the correct variable value from the
|
Ocelot will attempt to replace the placeholder with the correct variable value from the
|
||||||
Upstream URL when the request comes in.
|
Upstream URL when the request comes in.
|
||||||
|
|
||||||
@ -190,7 +190,7 @@ and LeastConnection algorithm you can use. If no load balancer is specified Ocel
|
|||||||
{
|
{
|
||||||
"DownstreamPathTemplate": "/api/posts/{postId}",
|
"DownstreamPathTemplate": "/api/posts/{postId}",
|
||||||
"DownstreamScheme": "https",
|
"DownstreamScheme": "https",
|
||||||
"UpstreamTemplate": "/posts/{postId}",
|
"UpstreamPathTemplate": "/posts/{postId}",
|
||||||
"UpstreamHttpMethod": "Put",
|
"UpstreamHttpMethod": "Put",
|
||||||
"ServiceName": "product"
|
"ServiceName": "product"
|
||||||
"LoadBalancer": "LeastConnection"
|
"LoadBalancer": "LeastConnection"
|
||||||
@ -305,19 +305,25 @@ Below is an example configuration that will transforms claims to query string pa
|
|||||||
This shows a transform where Ocelot looks at the users LocationId claim and add its as
|
This shows a transform where Ocelot looks at the users LocationId claim and add its as
|
||||||
a query string parameter to be forwarded onto the downstream service.
|
a query string parameter to be forwarded onto the downstream service.
|
||||||
|
|
||||||
## Logging
|
## Quality of Service
|
||||||
|
|
||||||
Ocelot uses the standard logging interfaces ILoggerFactory / ILogger<T> at the moment.
|
Ocelot supports one QoS capability at the current time. You can set on a per ReRoute basis if you
|
||||||
This is encapsulated in IOcelotLogger / IOcelotLoggerFactory with an implementation
|
want to use a circuit breaker when making requests to a downstream service. This uses the an awesome
|
||||||
for the standard asp.net core logging stuff at the moment.
|
.NET library called Polly check them out [here](https://github.com/App-vNext/Polly).
|
||||||
|
|
||||||
There are a bunch of debugging logs in the ocelot middlewares however I think the
|
Add the following section to a ReRoute configuration.
|
||||||
system probably needs more logging in the code it calls into. Other than the debugging
|
|
||||||
there is a global error handler that should catch any errors thrown and log them as errors.
|
|
||||||
|
|
||||||
The reason for not just using bog standard framework logging is that I could not
|
"QoSOptions": {
|
||||||
work out how to override the request id that get's logged when setting IncludeScopes
|
"ExceptionsAllowedBeforeBreaking":3,
|
||||||
to true for logging settings. Nicely onto the next feature.
|
"DurationOfBreak":5,
|
||||||
|
"TimeoutValue":5000
|
||||||
|
}
|
||||||
|
|
||||||
|
You must set a number greater than 0 against ExceptionsAllowedBeforeBreaking for this rule to be
|
||||||
|
implemented. Duration of break is how long the circuit breaker will stay open for after it is tripped.
|
||||||
|
TimeoutValue means ff a request takes more than 5 seconds it will automatically be timed out.
|
||||||
|
|
||||||
|
If you do not add a QoS section QoS will not be used.
|
||||||
|
|
||||||
## RequestId / CorrelationId
|
## RequestId / CorrelationId
|
||||||
|
|
||||||
@ -404,6 +410,20 @@ http request before it is passed to Ocelots request creator.
|
|||||||
Obviously you can just add middleware as normal before the call to app.UseOcelot() It cannot be added
|
Obviously you can just add middleware as normal before the call to app.UseOcelot() It cannot be added
|
||||||
after as Ocelot does not call the next middleware.
|
after as Ocelot does not call the next middleware.
|
||||||
|
|
||||||
|
## Logging
|
||||||
|
|
||||||
|
Ocelot uses the standard logging interfaces ILoggerFactory / ILogger<T> at the moment.
|
||||||
|
This is encapsulated in IOcelotLogger / IOcelotLoggerFactory with an implementation
|
||||||
|
for the standard asp.net core logging stuff at the moment.
|
||||||
|
|
||||||
|
There are a bunch of debugging logs in the ocelot middlewares however I think the
|
||||||
|
system probably needs more logging in the code it calls into. Other than the debugging
|
||||||
|
there is a global error handler that should catch any errors thrown and log them as errors.
|
||||||
|
|
||||||
|
The reason for not just using bog standard framework logging is that I could not
|
||||||
|
work out how to override the request id that get's logged when setting IncludeScopes
|
||||||
|
to true for logging settings. Nicely onto the next feature.
|
||||||
|
|
||||||
## Not supported
|
## Not supported
|
||||||
|
|
||||||
Ocelot does not support...
|
Ocelot does not support...
|
||||||
|
13
build.cake
13
build.cake
@ -42,7 +42,7 @@ var nugetFeedStableSymbolsUploadUrl = "https://www.nuget.org/api/v2/package";
|
|||||||
|
|
||||||
// internal build variables - don't change these.
|
// internal build variables - don't change these.
|
||||||
var releaseTag = "";
|
var releaseTag = "";
|
||||||
var committedVersion = "0.0.0-dev";
|
string committedVersion = "0.0.0-dev";
|
||||||
var buildVersion = committedVersion;
|
var buildVersion = committedVersion;
|
||||||
|
|
||||||
var target = Argument("target", "Default");
|
var target = Argument("target", "Default");
|
||||||
@ -310,7 +310,8 @@ private void PublishPackages(string feedApiKey, string codeFeedUrl, string symbo
|
|||||||
.ToDictionary(v => v[0], v => v[1]);
|
.ToDictionary(v => v[0], v => v[1]);
|
||||||
|
|
||||||
var codePackage = packagesDir + File(artifacts["nuget"]);
|
var codePackage = packagesDir + File(artifacts["nuget"]);
|
||||||
var symbolsPackage = packagesDir + File(artifacts["nugetSymbols"]);
|
|
||||||
|
Information("Pushing package");
|
||||||
|
|
||||||
NuGetPush(
|
NuGetPush(
|
||||||
codePackage,
|
codePackage,
|
||||||
@ -318,14 +319,6 @@ private void PublishPackages(string feedApiKey, string codeFeedUrl, string symbo
|
|||||||
ApiKey = feedApiKey,
|
ApiKey = feedApiKey,
|
||||||
Source = codeFeedUrl
|
Source = codeFeedUrl
|
||||||
});
|
});
|
||||||
|
|
||||||
NuGetPush(
|
|
||||||
symbolsPackage,
|
|
||||||
new NuGetPushSettings {
|
|
||||||
ApiKey = feedApiKey,
|
|
||||||
Source = symbolFeedUrl
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// gets the resource from the specified url
|
/// gets the resource from the specified url
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
# The path template we are listening on for this re route, Ocelot will add a trailing
|
# The path template we are listening on for this re route, Ocelot will add a trailing
|
||||||
# slash to this property. Then when a request is made Ocelot makes sure a trailing
|
# slash to this property. Then when a request is made Ocelot makes sure a trailing
|
||||||
# slash is added, so everything matches
|
# slash is added, so everything matches
|
||||||
"UpstreamTemplate": "/identityserverexample",
|
"UpstreamPathTemplate": "/identityserverexample",
|
||||||
# The method we are listening for on this re route
|
# The method we are listening for on this re route
|
||||||
"UpstreamHttpMethod": "Get",
|
"UpstreamHttpMethod": "Get",
|
||||||
# Only support identity server at the moment
|
# Only support identity server at the moment
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"projects": [ "src", "test" ],
|
"projects": [ "src", "test" ],
|
||||||
"sdk": {
|
"sdk": {
|
||||||
"version": "1.0.0-preview2-003133"
|
"version": "1.0.0-preview2-003131"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ namespace Ocelot.Authentication.Handler.Creator
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class AuthenticationHandlerCreator : IAuthenticationHandlerCreator
|
public class AuthenticationHandlerCreator : IAuthenticationHandlerCreator
|
||||||
{
|
{
|
||||||
public Response<RequestDelegate> CreateIdentityServerAuthenticationHandler(IApplicationBuilder app, AuthenticationOptions authOptions)
|
public Response<RequestDelegate> Create(IApplicationBuilder app, AuthenticationOptions authOptions)
|
||||||
{
|
{
|
||||||
var builder = app.New();
|
var builder = app.New();
|
||||||
|
|
||||||
|
@ -8,6 +8,6 @@ namespace Ocelot.Authentication.Handler.Creator
|
|||||||
|
|
||||||
public interface IAuthenticationHandlerCreator
|
public interface IAuthenticationHandlerCreator
|
||||||
{
|
{
|
||||||
Response<RequestDelegate> CreateIdentityServerAuthenticationHandler(IApplicationBuilder app, AuthenticationOptions authOptions);
|
Response<RequestDelegate> Create(IApplicationBuilder app, AuthenticationOptions authOptions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ namespace Ocelot.Authentication.Handler.Factory
|
|||||||
|
|
||||||
public Response<AuthenticationHandler> Get(IApplicationBuilder app, AuthenticationOptions authOptions)
|
public Response<AuthenticationHandler> Get(IApplicationBuilder app, AuthenticationOptions authOptions)
|
||||||
{
|
{
|
||||||
var handler = _creator.CreateIdentityServerAuthenticationHandler(app, authOptions);
|
var handler = _creator.Create(app, authOptions);
|
||||||
|
|
||||||
if (!handler.IsError)
|
if (!handler.IsError)
|
||||||
{
|
{
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Ocelot.Authentication.Handler.Factory;
|
using Ocelot.Authentication.Handler.Factory;
|
||||||
using Ocelot.Configuration;
|
using Ocelot.Configuration;
|
||||||
using Ocelot.Errors;
|
using Ocelot.Errors;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using Ocelot.Errors;
|
using Ocelot.Errors;
|
||||||
using Ocelot.Responses;
|
using Ocelot.Responses;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using Microsoft.Extensions.Logging;
|
using Ocelot.Infrastructure.RequestData;
|
||||||
using Ocelot.Infrastructure.RequestData;
|
|
||||||
using Ocelot.Logging;
|
using Ocelot.Logging;
|
||||||
using Ocelot.Responses;
|
using Ocelot.Responses;
|
||||||
|
|
||||||
@ -61,7 +60,7 @@ namespace Ocelot.Authorisation.Middleware
|
|||||||
SetPipelineError(new List<Error>
|
SetPipelineError(new List<Error>
|
||||||
{
|
{
|
||||||
new UnauthorisedError(
|
new UnauthorisedError(
|
||||||
$"{context.User.Identity.Name} unable to access {DownstreamRoute.ReRoute.UpstreamTemplate}")
|
$"{context.User.Identity.Name} unable to access {DownstreamRoute.ReRoute.UpstreamPathTemplate.Value}")
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,56 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Ocelot.Configuration.Builder
|
||||||
|
{
|
||||||
|
public class AuthenticationOptionsBuilder
|
||||||
|
{
|
||||||
|
|
||||||
|
private string _provider;
|
||||||
|
private string _providerRootUrl;
|
||||||
|
private string _scopeName;
|
||||||
|
private string _scopeSecret;
|
||||||
|
private bool _requireHttps;
|
||||||
|
private List<string> _additionalScopes;
|
||||||
|
|
||||||
|
public AuthenticationOptionsBuilder WithProvider(string provider)
|
||||||
|
{
|
||||||
|
_provider = provider;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AuthenticationOptionsBuilder WithProviderRootUrl(string providerRootUrl)
|
||||||
|
{
|
||||||
|
_providerRootUrl = providerRootUrl;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AuthenticationOptionsBuilder WithScopeName(string scopeName)
|
||||||
|
{
|
||||||
|
_scopeName = scopeName;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AuthenticationOptionsBuilder WithScopeSecret(string scopeSecret)
|
||||||
|
{
|
||||||
|
_scopeSecret = scopeSecret;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AuthenticationOptionsBuilder WithRequireHttps(bool requireHttps)
|
||||||
|
{
|
||||||
|
_requireHttps = requireHttps;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AuthenticationOptionsBuilder WithAdditionalScopes(List<string> additionalScopes)
|
||||||
|
{
|
||||||
|
_additionalScopes = additionalScopes;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AuthenticationOptions Build()
|
||||||
|
{
|
||||||
|
return new AuthenticationOptions(_provider, _providerRootUrl, _scopeName, _requireHttps, _additionalScopes, _scopeSecret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
34
src/Ocelot/Configuration/Builder/QoSOptionsBuilder.cs
Normal file
34
src/Ocelot/Configuration/Builder/QoSOptionsBuilder.cs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
namespace Ocelot.Configuration.Builder
|
||||||
|
{
|
||||||
|
public class QoSOptionsBuilder
|
||||||
|
{
|
||||||
|
private int _exceptionsAllowedBeforeBreaking;
|
||||||
|
|
||||||
|
private int _durationOfBreak;
|
||||||
|
|
||||||
|
private int _timeoutValue;
|
||||||
|
|
||||||
|
public QoSOptionsBuilder WithExceptionsAllowedBeforeBreaking(int exceptionsAllowedBeforeBreaking)
|
||||||
|
{
|
||||||
|
_exceptionsAllowedBeforeBreaking = exceptionsAllowedBeforeBreaking;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public QoSOptionsBuilder WithDurationOfBreak(int durationOfBreak)
|
||||||
|
{
|
||||||
|
_durationOfBreak = durationOfBreak;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public QoSOptionsBuilder WithTimeoutValue(int timeoutValue)
|
||||||
|
{
|
||||||
|
_timeoutValue = timeoutValue;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public QoSOptions Build()
|
||||||
|
{
|
||||||
|
return new QoSOptions(_exceptionsAllowedBeforeBreaking, _durationOfBreak, _timeoutValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
namespace Ocelot.Configuration.Builder
|
||||||
|
{
|
||||||
|
public class ServiceProviderConfiguraionBuilder
|
||||||
|
{
|
||||||
|
private string _serviceName;
|
||||||
|
private string _downstreamHost;
|
||||||
|
private int _downstreamPort;
|
||||||
|
private bool _userServiceDiscovery;
|
||||||
|
private string _serviceDiscoveryProvider;
|
||||||
|
private string _serviceDiscoveryProviderHost;
|
||||||
|
private int _serviceDiscoveryProviderPort;
|
||||||
|
|
||||||
|
public ServiceProviderConfiguraionBuilder WithServiceName(string serviceName)
|
||||||
|
{
|
||||||
|
_serviceName = serviceName;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServiceProviderConfiguraionBuilder WithDownstreamHost(string downstreamHost)
|
||||||
|
{
|
||||||
|
_downstreamHost = downstreamHost;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServiceProviderConfiguraionBuilder WithDownstreamPort(int downstreamPort)
|
||||||
|
{
|
||||||
|
_downstreamPort = downstreamPort;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServiceProviderConfiguraionBuilder WithUseServiceDiscovery(bool userServiceDiscovery)
|
||||||
|
{
|
||||||
|
_userServiceDiscovery = userServiceDiscovery;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServiceProviderConfiguraionBuilder WithServiceDiscoveryProvider(string serviceDiscoveryProvider)
|
||||||
|
{
|
||||||
|
_serviceDiscoveryProvider = serviceDiscoveryProvider;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServiceProviderConfiguraionBuilder WithServiceDiscoveryProviderHost(string serviceDiscoveryProviderHost)
|
||||||
|
{
|
||||||
|
_serviceDiscoveryProviderHost = serviceDiscoveryProviderHost;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServiceProviderConfiguraionBuilder WithServiceDiscoveryProviderPort(int serviceDiscoveryProviderPort)
|
||||||
|
{
|
||||||
|
_serviceDiscoveryProviderPort = serviceDiscoveryProviderPort;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public ServiceProviderConfiguraion Build()
|
||||||
|
{
|
||||||
|
return new ServiceProviderConfiguraion(_serviceName, _downstreamHost, _downstreamPort, _userServiceDiscovery,
|
||||||
|
_serviceDiscoveryProvider, _serviceDiscoveryProviderHost,_serviceDiscoveryProviderPort);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,356 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using Ocelot.Configuration.Builder;
|
||||||
|
using Ocelot.Configuration.File;
|
||||||
|
using Ocelot.Configuration.Parser;
|
||||||
|
using Ocelot.Configuration.Validator;
|
||||||
|
using Ocelot.LoadBalancer.LoadBalancers;
|
||||||
|
using Ocelot.Requester.QoS;
|
||||||
|
using Ocelot.Responses;
|
||||||
|
using Ocelot.Utilities;
|
||||||
|
|
||||||
|
namespace Ocelot.Configuration.Creator
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Register as singleton
|
||||||
|
/// </summary>
|
||||||
|
public class FileOcelotConfigurationCreator : IOcelotConfigurationCreator
|
||||||
|
{
|
||||||
|
private readonly IOptions<FileConfiguration> _options;
|
||||||
|
private readonly IConfigurationValidator _configurationValidator;
|
||||||
|
private const string RegExMatchEverything = ".*";
|
||||||
|
private const string RegExMatchEndString = "$";
|
||||||
|
private const string RegExIgnoreCase = "(?i)";
|
||||||
|
private const string RegExForwardSlashOnly = "^/$";
|
||||||
|
|
||||||
|
private readonly IClaimToThingConfigurationParser _claimToThingConfigurationParser;
|
||||||
|
private readonly ILogger<FileOcelotConfigurationCreator> _logger;
|
||||||
|
private readonly ILoadBalancerFactory _loadBalanceFactory;
|
||||||
|
private readonly ILoadBalancerHouse _loadBalancerHouse;
|
||||||
|
private readonly IQoSProviderFactory _qoSProviderFactory;
|
||||||
|
private readonly IQosProviderHouse _qosProviderHouse;
|
||||||
|
|
||||||
|
public FileOcelotConfigurationCreator(
|
||||||
|
IOptions<FileConfiguration> options,
|
||||||
|
IConfigurationValidator configurationValidator,
|
||||||
|
IClaimToThingConfigurationParser claimToThingConfigurationParser,
|
||||||
|
ILogger<FileOcelotConfigurationCreator> logger,
|
||||||
|
ILoadBalancerFactory loadBalancerFactory,
|
||||||
|
ILoadBalancerHouse loadBalancerHouse,
|
||||||
|
IQoSProviderFactory qoSProviderFactory,
|
||||||
|
IQosProviderHouse qosProviderHouse)
|
||||||
|
{
|
||||||
|
_loadBalanceFactory = loadBalancerFactory;
|
||||||
|
_loadBalancerHouse = loadBalancerHouse;
|
||||||
|
_qoSProviderFactory = qoSProviderFactory;
|
||||||
|
_qosProviderHouse = qosProviderHouse;
|
||||||
|
_options = options;
|
||||||
|
_configurationValidator = configurationValidator;
|
||||||
|
_claimToThingConfigurationParser = claimToThingConfigurationParser;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Response<IOcelotConfiguration>> Create()
|
||||||
|
{
|
||||||
|
var config = await SetUpConfiguration();
|
||||||
|
|
||||||
|
return new OkResponse<IOcelotConfiguration>(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<IOcelotConfiguration> SetUpConfiguration()
|
||||||
|
{
|
||||||
|
var response = _configurationValidator.IsValid(_options.Value);
|
||||||
|
|
||||||
|
if (response.Data.IsError)
|
||||||
|
{
|
||||||
|
var errorBuilder = new StringBuilder();
|
||||||
|
|
||||||
|
foreach (var error in response.Errors)
|
||||||
|
{
|
||||||
|
errorBuilder.AppendLine(error.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Exception($"Unable to start Ocelot..configuration, errors were {errorBuilder}");
|
||||||
|
}
|
||||||
|
|
||||||
|
var reRoutes = new List<ReRoute>();
|
||||||
|
|
||||||
|
foreach (var reRoute in _options.Value.ReRoutes)
|
||||||
|
{
|
||||||
|
var ocelotReRoute = await SetUpReRoute(reRoute, _options.Value.GlobalConfiguration);
|
||||||
|
reRoutes.Add(ocelotReRoute);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new OcelotConfiguration(reRoutes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<ReRoute> SetUpReRoute(FileReRoute fileReRoute, FileGlobalConfiguration globalConfiguration)
|
||||||
|
{
|
||||||
|
var isAuthenticated = IsAuthenticated(fileReRoute);
|
||||||
|
|
||||||
|
var isAuthorised = IsAuthorised(fileReRoute);
|
||||||
|
|
||||||
|
var isCached = IsCached(fileReRoute);
|
||||||
|
|
||||||
|
var requestIdKey = BuildRequestId(fileReRoute, globalConfiguration);
|
||||||
|
|
||||||
|
var reRouteKey = BuildReRouteKey(fileReRoute);
|
||||||
|
|
||||||
|
var upstreamTemplatePattern = BuildUpstreamTemplatePattern(fileReRoute);
|
||||||
|
|
||||||
|
var isQos = IsQoS(fileReRoute);
|
||||||
|
|
||||||
|
var serviceProviderConfiguration = BuildServiceProviderConfiguration(fileReRoute, globalConfiguration);
|
||||||
|
|
||||||
|
var authOptionsForRoute = BuildAuthenticationOptions(fileReRoute);
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
|
ReRoute reRoute;
|
||||||
|
|
||||||
|
var enableRateLimiting = (fileReRoute.RateLimitOptions!= null && fileReRoute.RateLimitOptions.EnableRateLimiting)? true: false;
|
||||||
|
RateLimitOptions rateLimitOption = null;
|
||||||
|
if (enableRateLimiting)
|
||||||
|
{
|
||||||
|
rateLimitOption = new RateLimitOptions(enableRateLimiting, globalConfiguration.RateLimitOptions.ClientIdHeader,
|
||||||
|
fileReRoute.RateLimitOptions.ClientWhitelist, globalConfiguration.RateLimitOptions.DisableRateLimitHeaders,
|
||||||
|
globalConfiguration.RateLimitOptions.QuotaExceededMessage, globalConfiguration.RateLimitOptions.RateLimitCounterPrefix,
|
||||||
|
new RateLimitRule(fileReRoute.RateLimitOptions.Period, TimeSpan.FromSeconds(fileReRoute.RateLimitOptions.PeriodTimespan), fileReRoute.RateLimitOptions.Limit)
|
||||||
|
, globalConfiguration.RateLimitOptions.HttpStatusCode);
|
||||||
|
}
|
||||||
|
var serviceProviderPort = globalConfiguration?.ServiceDiscoveryProvider?.Port ?? 0;
|
||||||
|
=======
|
||||||
|
var claimsToHeaders = BuildAddThingsToRequest(fileReRoute.AddHeadersToRequest);
|
||||||
|
|
||||||
|
var claimsToClaims = BuildAddThingsToRequest(fileReRoute.AddClaimsToRequest);
|
||||||
|
|
||||||
|
var claimsToQueries = BuildAddThingsToRequest(fileReRoute.AddQueriesToRequest);
|
||||||
|
|
||||||
|
var qosOptions = BuildQoSOptions(fileReRoute);
|
||||||
|
|
||||||
|
var reRoute = new ReRouteBuilder()
|
||||||
|
.WithDownstreamPathTemplate(fileReRoute.DownstreamPathTemplate)
|
||||||
|
.WithUpstreamPathTemplate(fileReRoute.UpstreamPathTemplate)
|
||||||
|
.WithUpstreamHttpMethod(fileReRoute.UpstreamHttpMethod)
|
||||||
|
.WithUpstreamTemplatePattern(upstreamTemplatePattern)
|
||||||
|
.WithIsAuthenticated(isAuthenticated)
|
||||||
|
.WithAuthenticationOptions(authOptionsForRoute)
|
||||||
|
.WithClaimsToHeaders(claimsToHeaders)
|
||||||
|
.WithClaimsToClaims(claimsToClaims)
|
||||||
|
.WithRouteClaimsRequirement(fileReRoute.RouteClaimsRequirement)
|
||||||
|
.WithIsAuthorised(isAuthorised)
|
||||||
|
.WithClaimsToQueries(claimsToQueries)
|
||||||
|
.WithRequestIdKey(requestIdKey)
|
||||||
|
.WithIsCached(isCached)
|
||||||
|
.WithCacheOptions(new CacheOptions(fileReRoute.FileCacheOptions.TtlSeconds))
|
||||||
|
.WithDownstreamScheme(fileReRoute.DownstreamScheme)
|
||||||
|
.WithLoadBalancer(fileReRoute.LoadBalancer)
|
||||||
|
.WithDownstreamHost(fileReRoute.DownstreamHost)
|
||||||
|
.WithDownstreamPort(fileReRoute.DownstreamPort)
|
||||||
|
.WithLoadBalancerKey(reRouteKey)
|
||||||
|
.WithServiceProviderConfiguraion(serviceProviderConfiguration)
|
||||||
|
.WithIsQos(isQos)
|
||||||
|
.WithQosOptions(qosOptions)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
await SetupLoadBalancer(reRoute);
|
||||||
|
SetupQosProvider(reRoute);
|
||||||
|
return reRoute;
|
||||||
|
}
|
||||||
|
|
||||||
|
private QoSOptions BuildQoSOptions(FileReRoute fileReRoute)
|
||||||
|
{
|
||||||
|
return new QoSOptionsBuilder()
|
||||||
|
.WithExceptionsAllowedBeforeBreaking(fileReRoute.QoSOptions.ExceptionsAllowedBeforeBreaking)
|
||||||
|
.WithDurationOfBreak(fileReRoute.QoSOptions.DurationOfBreak)
|
||||||
|
.WithTimeoutValue(fileReRoute.QoSOptions.TimeoutValue)
|
||||||
|
.Build();
|
||||||
|
}
|
||||||
|
>>>>>>> refs/remotes/origin/develop
|
||||||
|
|
||||||
|
private bool IsQoS(FileReRoute fileReRoute)
|
||||||
|
{
|
||||||
|
return fileReRoute.QoSOptions?.ExceptionsAllowedBeforeBreaking > 0 && fileReRoute.QoSOptions?.TimeoutValue > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
|
if (isAuthenticated)
|
||||||
|
{
|
||||||
|
var authOptionsForRoute = new AuthenticationOptions(fileReRoute.AuthenticationOptions.Provider,
|
||||||
|
fileReRoute.AuthenticationOptions.ProviderRootUrl, fileReRoute.AuthenticationOptions.ScopeName,
|
||||||
|
fileReRoute.AuthenticationOptions.RequireHttps, fileReRoute.AuthenticationOptions.AdditionalScopes,
|
||||||
|
fileReRoute.AuthenticationOptions.ScopeSecret);
|
||||||
|
|
||||||
|
var claimsToHeaders = GetAddThingsToRequest(fileReRoute.AddHeadersToRequest);
|
||||||
|
var claimsToClaims = GetAddThingsToRequest(fileReRoute.AddClaimsToRequest);
|
||||||
|
var claimsToQueries = GetAddThingsToRequest(fileReRoute.AddQueriesToRequest);
|
||||||
|
|
||||||
|
reRoute = new ReRoute(new DownstreamPathTemplate(fileReRoute.DownstreamPathTemplate),
|
||||||
|
fileReRoute.UpstreamTemplate,
|
||||||
|
fileReRoute.UpstreamHttpMethod, upstreamTemplate, isAuthenticated,
|
||||||
|
authOptionsForRoute, claimsToHeaders, claimsToClaims,
|
||||||
|
fileReRoute.RouteClaimsRequirement, isAuthorised, claimsToQueries,
|
||||||
|
requestIdKey, isCached, new CacheOptions(fileReRoute.FileCacheOptions.TtlSeconds)
|
||||||
|
, fileReRoute.DownstreamScheme,
|
||||||
|
fileReRoute.LoadBalancer, fileReRoute.DownstreamHost, fileReRoute.DownstreamPort, loadBalancerKey,
|
||||||
|
serviceProviderConfiguration, isQos,
|
||||||
|
new QoSOptions(fileReRoute.QoSOptions.ExceptionsAllowedBeforeBreaking, fileReRoute.QoSOptions.DurationOfBreak, fileReRoute.QoSOptions.TimeoutValue),
|
||||||
|
enableRateLimiting, rateLimitOption);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
reRoute = new ReRoute(new DownstreamPathTemplate(fileReRoute.DownstreamPathTemplate),
|
||||||
|
fileReRoute.UpstreamTemplate,
|
||||||
|
fileReRoute.UpstreamHttpMethod, upstreamTemplate, isAuthenticated,
|
||||||
|
null, new List<ClaimToThing>(), new List<ClaimToThing>(),
|
||||||
|
fileReRoute.RouteClaimsRequirement, isAuthorised, new List<ClaimToThing>(),
|
||||||
|
requestIdKey, isCached, new CacheOptions(fileReRoute.FileCacheOptions.TtlSeconds),
|
||||||
|
fileReRoute.DownstreamScheme,
|
||||||
|
fileReRoute.LoadBalancer, fileReRoute.DownstreamHost, fileReRoute.DownstreamPort, loadBalancerKey,
|
||||||
|
serviceProviderConfiguration, isQos,
|
||||||
|
new QoSOptions(fileReRoute.QoSOptions.ExceptionsAllowedBeforeBreaking, fileReRoute.QoSOptions.DurationOfBreak, fileReRoute.QoSOptions.TimeoutValue),
|
||||||
|
enableRateLimiting, rateLimitOption);
|
||||||
|
}
|
||||||
|
=======
|
||||||
|
private bool IsAuthenticated(FileReRoute fileReRoute)
|
||||||
|
{
|
||||||
|
return !string.IsNullOrEmpty(fileReRoute.AuthenticationOptions?.Provider);
|
||||||
|
}
|
||||||
|
>>>>>>> refs/remotes/origin/develop
|
||||||
|
|
||||||
|
private bool IsAuthorised(FileReRoute fileReRoute)
|
||||||
|
{
|
||||||
|
return fileReRoute.RouteClaimsRequirement?.Count > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsCached(FileReRoute fileReRoute)
|
||||||
|
{
|
||||||
|
return fileReRoute.FileCacheOptions.TtlSeconds > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string BuildRequestId(FileReRoute fileReRoute, FileGlobalConfiguration globalConfiguration)
|
||||||
|
{
|
||||||
|
var globalRequestIdConfiguration = !string.IsNullOrEmpty(globalConfiguration?.RequestIdKey);
|
||||||
|
|
||||||
|
var requestIdKey = globalRequestIdConfiguration
|
||||||
|
? globalConfiguration.RequestIdKey
|
||||||
|
: fileReRoute.RequestIdKey;
|
||||||
|
|
||||||
|
return requestIdKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string BuildReRouteKey(FileReRoute fileReRoute)
|
||||||
|
{
|
||||||
|
//note - not sure if this is the correct key, but this is probably the only unique key i can think of given my poor brain
|
||||||
|
var loadBalancerKey = $"{fileReRoute.UpstreamPathTemplate}{fileReRoute.UpstreamHttpMethod}";
|
||||||
|
return loadBalancerKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
private AuthenticationOptions BuildAuthenticationOptions(FileReRoute fileReRoute)
|
||||||
|
{
|
||||||
|
return new AuthenticationOptionsBuilder()
|
||||||
|
.WithProvider(fileReRoute.AuthenticationOptions?.Provider)
|
||||||
|
.WithProviderRootUrl(fileReRoute.AuthenticationOptions?.ProviderRootUrl)
|
||||||
|
.WithScopeName(fileReRoute.AuthenticationOptions?.ScopeName)
|
||||||
|
.WithRequireHttps(fileReRoute.AuthenticationOptions.RequireHttps)
|
||||||
|
.WithAdditionalScopes(fileReRoute.AuthenticationOptions?.AdditionalScopes)
|
||||||
|
.WithScopeSecret(fileReRoute.AuthenticationOptions?.ScopeSecret)
|
||||||
|
.Build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SetupLoadBalancer(ReRoute reRoute)
|
||||||
|
{
|
||||||
|
var loadBalancer = await _loadBalanceFactory.Get(reRoute);
|
||||||
|
_loadBalancerHouse.Add(reRoute.ReRouteKey, loadBalancer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetupQosProvider(ReRoute reRoute)
|
||||||
|
{
|
||||||
|
var loadBalancer = _qoSProviderFactory.Get(reRoute);
|
||||||
|
_qosProviderHouse.Add(reRoute.ReRouteKey, loadBalancer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ServiceProviderConfiguraion BuildServiceProviderConfiguration(FileReRoute fileReRoute, FileGlobalConfiguration globalConfiguration)
|
||||||
|
{
|
||||||
|
var useServiceDiscovery = !string.IsNullOrEmpty(fileReRoute.ServiceName)
|
||||||
|
&& !string.IsNullOrEmpty(globalConfiguration?.ServiceDiscoveryProvider?.Provider);
|
||||||
|
|
||||||
|
var serviceProviderPort = globalConfiguration?.ServiceDiscoveryProvider?.Port ?? 0;
|
||||||
|
|
||||||
|
return new ServiceProviderConfiguraionBuilder()
|
||||||
|
.WithServiceName(fileReRoute.ServiceName)
|
||||||
|
.WithDownstreamHost(fileReRoute.DownstreamHost)
|
||||||
|
.WithDownstreamPort(fileReRoute.DownstreamPort)
|
||||||
|
.WithUseServiceDiscovery(useServiceDiscovery)
|
||||||
|
.WithServiceDiscoveryProvider(globalConfiguration?.ServiceDiscoveryProvider?.Provider)
|
||||||
|
.WithServiceDiscoveryProviderHost(globalConfiguration?.ServiceDiscoveryProvider?.Host)
|
||||||
|
.WithServiceDiscoveryProviderPort(serviceProviderPort)
|
||||||
|
.Build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private string BuildUpstreamTemplatePattern(FileReRoute reRoute)
|
||||||
|
{
|
||||||
|
var upstreamTemplate = reRoute.UpstreamPathTemplate;
|
||||||
|
|
||||||
|
upstreamTemplate = upstreamTemplate.SetLastCharacterAs('/');
|
||||||
|
|
||||||
|
var placeholders = new List<string>();
|
||||||
|
|
||||||
|
for (var i = 0; i < upstreamTemplate.Length; i++)
|
||||||
|
{
|
||||||
|
if (IsPlaceHolder(upstreamTemplate, i))
|
||||||
|
{
|
||||||
|
var postitionOfPlaceHolderClosingBracket = upstreamTemplate.IndexOf('}', i);
|
||||||
|
var difference = postitionOfPlaceHolderClosingBracket - i + 1;
|
||||||
|
var variableName = upstreamTemplate.Substring(i, difference);
|
||||||
|
placeholders.Add(variableName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var placeholder in placeholders)
|
||||||
|
{
|
||||||
|
upstreamTemplate = upstreamTemplate.Replace(placeholder, RegExMatchEverything);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (upstreamTemplate == "/")
|
||||||
|
{
|
||||||
|
return RegExForwardSlashOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
var route = reRoute.ReRouteIsCaseSensitive
|
||||||
|
? $"{upstreamTemplate}{RegExMatchEndString}"
|
||||||
|
: $"{RegExIgnoreCase}{upstreamTemplate}{RegExMatchEndString}";
|
||||||
|
|
||||||
|
return route;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ClaimToThing> BuildAddThingsToRequest(Dictionary<string,string> thingBeingAdded)
|
||||||
|
{
|
||||||
|
var claimsToTHings = new List<ClaimToThing>();
|
||||||
|
|
||||||
|
foreach (var add in thingBeingAdded)
|
||||||
|
{
|
||||||
|
var claimToHeader = _claimToThingConfigurationParser.Extract(add.Key, add.Value);
|
||||||
|
|
||||||
|
if (claimToHeader.IsError)
|
||||||
|
{
|
||||||
|
_logger.LogCritical(new EventId(1, "Application Failed to start"),
|
||||||
|
$"Unable to extract configuration for key: {add.Key} and value: {add.Value} your configuration file is incorrect");
|
||||||
|
|
||||||
|
throw new Exception(claimToHeader.Errors[0].Message);
|
||||||
|
}
|
||||||
|
claimsToTHings.Add(claimToHeader.Data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return claimsToTHings;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsPlaceHolder(string upstreamTemplate, int i)
|
||||||
|
{
|
||||||
|
return upstreamTemplate[i] == '{';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -17,7 +17,7 @@ namespace Ocelot.Configuration.File
|
|||||||
}
|
}
|
||||||
|
|
||||||
public string DownstreamPathTemplate { get; set; }
|
public string DownstreamPathTemplate { get; set; }
|
||||||
public string UpstreamTemplate { get; set; }
|
public string UpstreamPathTemplate { get; set; }
|
||||||
public string UpstreamHttpMethod { get; set; }
|
public string UpstreamHttpMethod { get; set; }
|
||||||
public FileAuthenticationOptions AuthenticationOptions { get; set; }
|
public FileAuthenticationOptions AuthenticationOptions { get; set; }
|
||||||
public Dictionary<string, string> AddHeadersToRequest { get; set; }
|
public Dictionary<string, string> AddHeadersToRequest { get; set; }
|
||||||
|
35
src/Ocelot/Configuration/QoSOptions.cs.bak
Normal file
35
src/Ocelot/Configuration/QoSOptions.cs.bak
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<<<<<<< HEAD
|
||||||
|
using Polly.Timeout;
|
||||||
|
using System;
|
||||||
|
=======
|
||||||
|
using System;
|
||||||
|
using Polly.Timeout;
|
||||||
|
>>>>>>> refs/remotes/origin/develop
|
||||||
|
|
||||||
|
namespace Ocelot.Configuration
|
||||||
|
{
|
||||||
|
public class QoSOptions
|
||||||
|
{
|
||||||
|
public QoSOptions(
|
||||||
|
int exceptionsAllowedBeforeBreaking,
|
||||||
|
int durationofBreak,
|
||||||
|
int timeoutValue,
|
||||||
|
TimeoutStrategy timeoutStrategy = TimeoutStrategy.Pessimistic)
|
||||||
|
{
|
||||||
|
ExceptionsAllowedBeforeBreaking = exceptionsAllowedBeforeBreaking;
|
||||||
|
DurationOfBreak = TimeSpan.FromMilliseconds(durationofBreak);
|
||||||
|
TimeoutValue = TimeSpan.FromMilliseconds(timeoutValue);
|
||||||
|
TimeoutStrategy = timeoutStrategy;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public int ExceptionsAllowedBeforeBreaking { get; private set; }
|
||||||
|
|
||||||
|
public TimeSpan DurationOfBreak { get; private set; }
|
||||||
|
|
||||||
|
public TimeSpan TimeoutValue { get; private set; }
|
||||||
|
|
||||||
|
public TimeoutStrategy TimeoutStrategy { get; private set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
94
src/Ocelot/Configuration/ReRoute.cs.bak
Normal file
94
src/Ocelot/Configuration/ReRoute.cs.bak
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Net.Http;
|
||||||
|
using Ocelot.Values;
|
||||||
|
|
||||||
|
namespace Ocelot.Configuration
|
||||||
|
{
|
||||||
|
public class ReRoute
|
||||||
|
{
|
||||||
|
public ReRoute(PathTemplate downstreamPathTemplate,
|
||||||
|
PathTemplate upstreamTemplate,
|
||||||
|
HttpMethod upstreamHttpMethod,
|
||||||
|
string upstreamTemplatePattern,
|
||||||
|
bool isAuthenticated,
|
||||||
|
AuthenticationOptions authenticationOptions,
|
||||||
|
List<ClaimToThing> configurationHeaderExtractorProperties,
|
||||||
|
List<ClaimToThing> claimsToClaims,
|
||||||
|
Dictionary<string, string> routeClaimsRequirement,
|
||||||
|
bool isAuthorised,
|
||||||
|
List<ClaimToThing> claimsToQueries,
|
||||||
|
<<<<<<< HEAD
|
||||||
|
string requestIdKey, bool isCached, CacheOptions fileCacheOptions,
|
||||||
|
string downstreamScheme, string loadBalancer, string downstreamHost,
|
||||||
|
int downstreamPort, string loadBalancerKey, ServiceProviderConfiguraion serviceProviderConfiguraion,
|
||||||
|
bool isQos,QoSOptions qos, bool enableRateLimit, RateLimitOptions ratelimitOptions)
|
||||||
|
=======
|
||||||
|
string requestIdKey,
|
||||||
|
bool isCached,
|
||||||
|
CacheOptions fileCacheOptions,
|
||||||
|
string downstreamScheme,
|
||||||
|
string loadBalancer,
|
||||||
|
string downstreamHost,
|
||||||
|
int downstreamPort,
|
||||||
|
string reRouteKey,
|
||||||
|
ServiceProviderConfiguraion serviceProviderConfiguraion,
|
||||||
|
bool isQos,
|
||||||
|
QoSOptions qos)
|
||||||
|
>>>>>>> refs/remotes/origin/develop
|
||||||
|
{
|
||||||
|
ReRouteKey = reRouteKey;
|
||||||
|
ServiceProviderConfiguraion = serviceProviderConfiguraion;
|
||||||
|
LoadBalancer = loadBalancer;
|
||||||
|
DownstreamHost = downstreamHost;
|
||||||
|
DownstreamPort = downstreamPort;
|
||||||
|
DownstreamPathTemplate = downstreamPathTemplate;
|
||||||
|
UpstreamPathTemplate = upstreamTemplate;
|
||||||
|
UpstreamHttpMethod = upstreamHttpMethod;
|
||||||
|
UpstreamTemplatePattern = upstreamTemplatePattern;
|
||||||
|
IsAuthenticated = isAuthenticated;
|
||||||
|
AuthenticationOptions = authenticationOptions;
|
||||||
|
RouteClaimsRequirement = routeClaimsRequirement;
|
||||||
|
IsAuthorised = isAuthorised;
|
||||||
|
RequestIdKey = requestIdKey;
|
||||||
|
IsCached = isCached;
|
||||||
|
FileCacheOptions = fileCacheOptions;
|
||||||
|
ClaimsToQueries = claimsToQueries
|
||||||
|
?? new List<ClaimToThing>();
|
||||||
|
ClaimsToClaims = claimsToClaims
|
||||||
|
?? new List<ClaimToThing>();
|
||||||
|
ClaimsToHeaders = configurationHeaderExtractorProperties
|
||||||
|
?? new List<ClaimToThing>();
|
||||||
|
DownstreamScheme = downstreamScheme;
|
||||||
|
IsQos = isQos;
|
||||||
|
QosOptions = qos;
|
||||||
|
EnableEndpointRateLimiting = enableRateLimit;
|
||||||
|
RateLimitOptions = ratelimitOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ReRouteKey {get;private set;}
|
||||||
|
public PathTemplate DownstreamPathTemplate { get; private set; }
|
||||||
|
public PathTemplate UpstreamPathTemplate { get; private set; }
|
||||||
|
public string UpstreamTemplatePattern { get; private set; }
|
||||||
|
public HttpMethod UpstreamHttpMethod { get; private set; }
|
||||||
|
public bool IsAuthenticated { get; private set; }
|
||||||
|
public bool IsAuthorised { get; private set; }
|
||||||
|
public AuthenticationOptions AuthenticationOptions { get; private set; }
|
||||||
|
public List<ClaimToThing> ClaimsToQueries { get; private set; }
|
||||||
|
public List<ClaimToThing> ClaimsToHeaders { get; private set; }
|
||||||
|
public List<ClaimToThing> ClaimsToClaims { get; private set; }
|
||||||
|
public Dictionary<string, string> RouteClaimsRequirement { get; private set; }
|
||||||
|
public string RequestIdKey { get; private set; }
|
||||||
|
public bool IsCached { get; private set; }
|
||||||
|
public CacheOptions FileCacheOptions { get; private set; }
|
||||||
|
public string DownstreamScheme {get;private set;}
|
||||||
|
public bool IsQos { get; private set; }
|
||||||
|
public QoSOptions QosOptions { get; private set; }
|
||||||
|
public string LoadBalancer {get;private set;}
|
||||||
|
public string DownstreamHost { get; private set; }
|
||||||
|
public int DownstreamPort { get; private set; }
|
||||||
|
public ServiceProviderConfiguraion ServiceProviderConfiguraion { get; private set; }
|
||||||
|
public bool EnableEndpointRateLimiting { get; private set; }
|
||||||
|
public RateLimitOptions RateLimitOptions { get; private set; }
|
||||||
|
}
|
||||||
|
}
|
@ -54,7 +54,7 @@ namespace Ocelot.Configuration.Validator
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var error = new UnsupportedAuthenticationProviderError($"{reRoute.AuthenticationOptions?.Provider} is unsupported authentication provider, upstream template is {reRoute.UpstreamTemplate}, upstream method is {reRoute.UpstreamHttpMethod}");
|
var error = new UnsupportedAuthenticationProviderError($"{reRoute.AuthenticationOptions?.Provider} is unsupported authentication provider, upstream template is {reRoute.UpstreamPathTemplate}, upstream method is {reRoute.UpstreamHttpMethod}");
|
||||||
errors.Add(error);
|
errors.Add(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,18 +94,18 @@ namespace Ocelot.Configuration.Validator
|
|||||||
private ConfigurationValidationResult CheckForDupliateReRoutes(FileConfiguration configuration)
|
private ConfigurationValidationResult CheckForDupliateReRoutes(FileConfiguration configuration)
|
||||||
{
|
{
|
||||||
var hasDupes = configuration.ReRoutes
|
var hasDupes = configuration.ReRoutes
|
||||||
.GroupBy(x => new { x.UpstreamTemplate, x.UpstreamHttpMethod }).Any(x => x.Skip(1).Any());
|
.GroupBy(x => new { x.UpstreamPathTemplate, x.UpstreamHttpMethod }).Any(x => x.Skip(1).Any());
|
||||||
|
|
||||||
if (!hasDupes)
|
if (!hasDupes)
|
||||||
{
|
{
|
||||||
return new ConfigurationValidationResult(false);
|
return new ConfigurationValidationResult(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
var dupes = configuration.ReRoutes.GroupBy(x => new { x.UpstreamTemplate, x.UpstreamHttpMethod })
|
var dupes = configuration.ReRoutes.GroupBy(x => new { x.UpstreamPathTemplate, x.UpstreamHttpMethod })
|
||||||
.Where(x => x.Skip(1).Any());
|
.Where(x => x.Skip(1).Any());
|
||||||
|
|
||||||
var errors = dupes
|
var errors = dupes
|
||||||
.Select(d => new DownstreamPathTemplateAlreadyUsedError(string.Format("Duplicate DownstreamPath: {0}", d.Key.UpstreamTemplate)))
|
.Select(d => new DownstreamPathTemplateAlreadyUsedError(string.Format("Duplicate DownstreamPath: {0}", d.Key.UpstreamPathTemplate)))
|
||||||
.Cast<Error>()
|
.Cast<Error>()
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@ using Ocelot.Logging;
|
|||||||
using Ocelot.QueryStrings;
|
using Ocelot.QueryStrings;
|
||||||
using Ocelot.Request.Builder;
|
using Ocelot.Request.Builder;
|
||||||
using Ocelot.Requester;
|
using Ocelot.Requester;
|
||||||
|
using Ocelot.Requester.QoS;
|
||||||
using Ocelot.Responder;
|
using Ocelot.Responder;
|
||||||
using Ocelot.ServiceDiscovery;
|
using Ocelot.ServiceDiscovery;
|
||||||
using Ocelot.RateLimit;
|
using Ocelot.RateLimit;
|
||||||
@ -62,6 +63,8 @@ namespace Ocelot.DependencyInjection
|
|||||||
{
|
{
|
||||||
services.AddMvcCore().AddJsonFormatters();
|
services.AddMvcCore().AddJsonFormatters();
|
||||||
services.AddLogging();
|
services.AddLogging();
|
||||||
|
services.AddSingleton<IQosProviderHouse, QosProviderHouse>();
|
||||||
|
services.AddSingleton<IQoSProviderFactory, QoSProviderFactory>();
|
||||||
services.AddSingleton<IServiceDiscoveryProviderFactory, ServiceDiscoveryProviderFactory>();
|
services.AddSingleton<IServiceDiscoveryProviderFactory, ServiceDiscoveryProviderFactory>();
|
||||||
services.AddSingleton<ILoadBalancerFactory, LoadBalancerFactory>();
|
services.AddSingleton<ILoadBalancerFactory, LoadBalancerFactory>();
|
||||||
services.AddSingleton<ILoadBalancerHouse, LoadBalancerHouse>();
|
services.AddSingleton<ILoadBalancerHouse, LoadBalancerHouse>();
|
||||||
|
@ -26,15 +26,22 @@ namespace Ocelot.DownstreamRouteFinder.Finder
|
|||||||
{
|
{
|
||||||
var configuration = await _configProvider.Get();
|
var configuration = await _configProvider.Get();
|
||||||
|
|
||||||
var applicableReRoutes = configuration.Data.ReRoutes.Where(r => string.Equals(r.UpstreamHttpMethod, upstreamHttpMethod, StringComparison.CurrentCultureIgnoreCase));
|
var applicableReRoutes = configuration.Data.ReRoutes.Where(r => string.Equals(r.UpstreamHttpMethod.Method, upstreamHttpMethod, StringComparison.CurrentCultureIgnoreCase));
|
||||||
|
|
||||||
foreach (var reRoute in applicableReRoutes)
|
foreach (var reRoute in applicableReRoutes)
|
||||||
{
|
{
|
||||||
|
if (upstreamUrlPath == reRoute.UpstreamTemplatePattern)
|
||||||
|
{
|
||||||
|
var templateVariableNameAndValues = _urlPathPlaceholderNameAndValueFinder.Find(upstreamUrlPath, reRoute.UpstreamPathTemplate.Value);
|
||||||
|
|
||||||
|
return new OkResponse<DownstreamRoute>(new DownstreamRoute(templateVariableNameAndValues.Data, reRoute));
|
||||||
|
}
|
||||||
|
|
||||||
var urlMatch = _urlMatcher.Match(upstreamUrlPath, reRoute.UpstreamTemplatePattern);
|
var urlMatch = _urlMatcher.Match(upstreamUrlPath, reRoute.UpstreamTemplatePattern);
|
||||||
|
|
||||||
if (urlMatch.Data.Match)
|
if (urlMatch.Data.Match)
|
||||||
{
|
{
|
||||||
var templateVariableNameAndValues = _urlPathPlaceholderNameAndValueFinder.Find(upstreamUrlPath, reRoute.UpstreamTemplate);
|
var templateVariableNameAndValues = _urlPathPlaceholderNameAndValueFinder.Find(upstreamUrlPath, reRoute.UpstreamPathTemplate.Value);
|
||||||
|
|
||||||
return new OkResponse<DownstreamRoute>(new DownstreamRoute(templateVariableNameAndValues.Data, reRoute));
|
return new OkResponse<DownstreamRoute>(new DownstreamRoute(templateVariableNameAndValues.Data, reRoute));
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ namespace Ocelot.DownstreamUrlCreator.UrlTemplateReplacer
|
|||||||
{
|
{
|
||||||
public class DownstreamTemplatePathPlaceholderReplacer : IDownstreamPathPlaceholderReplacer
|
public class DownstreamTemplatePathPlaceholderReplacer : IDownstreamPathPlaceholderReplacer
|
||||||
{
|
{
|
||||||
public Response<DownstreamPath> Replace(DownstreamPathTemplate downstreamPathTemplate, List<UrlPathPlaceholderNameAndValue> urlPathPlaceholderNameAndValues)
|
public Response<DownstreamPath> Replace(PathTemplate downstreamPathTemplate, List<UrlPathPlaceholderNameAndValue> urlPathPlaceholderNameAndValues)
|
||||||
{
|
{
|
||||||
var downstreamPath = new StringBuilder();
|
var downstreamPath = new StringBuilder();
|
||||||
|
|
||||||
|
@ -7,6 +7,6 @@ namespace Ocelot.DownstreamUrlCreator.UrlTemplateReplacer
|
|||||||
{
|
{
|
||||||
public interface IDownstreamPathPlaceholderReplacer
|
public interface IDownstreamPathPlaceholderReplacer
|
||||||
{
|
{
|
||||||
Response<DownstreamPath> Replace(DownstreamPathTemplate downstreamPathTemplate, List<UrlPathPlaceholderNameAndValue> urlPathPlaceholderNameAndValues);
|
Response<DownstreamPath> Replace(PathTemplate downstreamPathTemplate, List<UrlPathPlaceholderNameAndValue> urlPathPlaceholderNameAndValues);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -25,6 +25,8 @@
|
|||||||
ServicesAreNullError,
|
ServicesAreNullError,
|
||||||
ServicesAreEmptyError,
|
ServicesAreEmptyError,
|
||||||
UnableToFindServiceDiscoveryProviderError,
|
UnableToFindServiceDiscoveryProviderError,
|
||||||
UnableToFindLoadBalancerError
|
UnableToFindLoadBalancerError,
|
||||||
|
RequestTimedOutError,
|
||||||
|
UnableToFindQoSProviderError
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,16 +14,7 @@ namespace Ocelot.LoadBalancer.LoadBalancers
|
|||||||
|
|
||||||
public async Task<ILoadBalancer> Get(ReRoute reRoute)
|
public async Task<ILoadBalancer> Get(ReRoute reRoute)
|
||||||
{
|
{
|
||||||
var serviceConfig = new ServiceProviderConfiguraion(
|
var serviceProvider = _serviceProviderFactory.Get(reRoute.ServiceProviderConfiguraion);
|
||||||
reRoute.ServiceProviderConfiguraion.ServiceName,
|
|
||||||
reRoute.ServiceProviderConfiguraion.DownstreamHost,
|
|
||||||
reRoute.ServiceProviderConfiguraion.DownstreamPort,
|
|
||||||
reRoute.ServiceProviderConfiguraion.UseServiceDiscovery,
|
|
||||||
reRoute.ServiceProviderConfiguraion.ServiceDiscoveryProvider,
|
|
||||||
reRoute.ServiceProviderConfiguraion.ServiceProviderHost,
|
|
||||||
reRoute.ServiceProviderConfiguraion.ServiceProviderPort);
|
|
||||||
|
|
||||||
var serviceProvider = _serviceProviderFactory.Get(serviceConfig);
|
|
||||||
|
|
||||||
switch (reRoute.LoadBalancer)
|
switch (reRoute.LoadBalancer)
|
||||||
{
|
{
|
||||||
|
@ -6,7 +6,6 @@ using Ocelot.LoadBalancer.LoadBalancers;
|
|||||||
using Ocelot.Logging;
|
using Ocelot.Logging;
|
||||||
using Ocelot.Middleware;
|
using Ocelot.Middleware;
|
||||||
using Ocelot.QueryStrings.Middleware;
|
using Ocelot.QueryStrings.Middleware;
|
||||||
using Ocelot.ServiceDiscovery;
|
|
||||||
|
|
||||||
namespace Ocelot.LoadBalancer.Middleware
|
namespace Ocelot.LoadBalancer.Middleware
|
||||||
{
|
{
|
||||||
@ -31,7 +30,7 @@ namespace Ocelot.LoadBalancer.Middleware
|
|||||||
{
|
{
|
||||||
_logger.LogDebug("started calling load balancing middleware");
|
_logger.LogDebug("started calling load balancing middleware");
|
||||||
|
|
||||||
var loadBalancer = _loadBalancerHouse.Get(DownstreamRoute.ReRoute.LoadBalancerKey);
|
var loadBalancer = _loadBalancerHouse.Get(DownstreamRoute.ReRoute.ReRouteKey);
|
||||||
if(loadBalancer.IsError)
|
if(loadBalancer.IsError)
|
||||||
{
|
{
|
||||||
SetPipelineError(loadBalancer.Errors);
|
SetPipelineError(loadBalancer.Errors);
|
||||||
|
@ -105,7 +105,7 @@ namespace Ocelot.RateLimit.Middleware
|
|||||||
|
|
||||||
public virtual void LogBlockedRequest(HttpContext httpContext, ClientRequestIdentity identity, RateLimitCounter counter, RateLimitRule rule)
|
public virtual void LogBlockedRequest(HttpContext httpContext, ClientRequestIdentity identity, RateLimitCounter counter, RateLimitRule rule)
|
||||||
{
|
{
|
||||||
_logger.LogDebug($"Request {identity.HttpVerb}:{identity.Path} from ClientId {identity.ClientId} has been blocked, quota {rule.Limit}/{rule.Period} exceeded by {counter.TotalRequests}. Blocked by rule { DownstreamRoute.ReRoute.UpstreamTemplate }, TraceIdentifier {httpContext.TraceIdentifier}.");
|
_logger.LogDebug($"Request {identity.HttpVerb}:{identity.Path} from ClientId {identity.ClientId} has been blocked, quota {rule.Limit}/{rule.Period} exceeded by {counter.TotalRequests}. Blocked by rule { DownstreamRoute.ReRoute.UpstreamPathTemplate }, TraceIdentifier {httpContext.TraceIdentifier}.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual Task ReturnQuotaExceededResponse(HttpContext httpContext, RateLimitOptions option, string retryAfter)
|
public virtual Task ReturnQuotaExceededResponse(HttpContext httpContext, RateLimitOptions option, string retryAfter)
|
||||||
|
@ -3,6 +3,7 @@ using System.Threading.Tasks;
|
|||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Ocelot.Responses;
|
using Ocelot.Responses;
|
||||||
using Ocelot.Configuration;
|
using Ocelot.Configuration;
|
||||||
|
using Ocelot.Requester.QoS;
|
||||||
|
|
||||||
namespace Ocelot.Request.Builder
|
namespace Ocelot.Request.Builder
|
||||||
{
|
{
|
||||||
@ -18,7 +19,7 @@ namespace Ocelot.Request.Builder
|
|||||||
string contentType,
|
string contentType,
|
||||||
RequestId.RequestId requestId,
|
RequestId.RequestId requestId,
|
||||||
bool isQos,
|
bool isQos,
|
||||||
QoSOptions qos)
|
IQoSProvider qosProvider)
|
||||||
{
|
{
|
||||||
var request = await new RequestBuilder()
|
var request = await new RequestBuilder()
|
||||||
.WithHttpMethod(httpMethod)
|
.WithHttpMethod(httpMethod)
|
||||||
@ -30,7 +31,7 @@ namespace Ocelot.Request.Builder
|
|||||||
.WithRequestId(requestId)
|
.WithRequestId(requestId)
|
||||||
.WithCookies(cookies)
|
.WithCookies(cookies)
|
||||||
.WithIsQos(isQos)
|
.WithIsQos(isQos)
|
||||||
.WithQos(qos)
|
.WithQos(qosProvider)
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
return new OkResponse<Request>(request);
|
return new OkResponse<Request>(request);
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Ocelot.Requester.QoS;
|
||||||
using Ocelot.Responses;
|
using Ocelot.Responses;
|
||||||
using Ocelot.Configuration;
|
|
||||||
|
|
||||||
namespace Ocelot.Request.Builder
|
namespace Ocelot.Request.Builder
|
||||||
{
|
{
|
||||||
@ -17,6 +17,6 @@ namespace Ocelot.Request.Builder
|
|||||||
string contentType,
|
string contentType,
|
||||||
RequestId.RequestId requestId,
|
RequestId.RequestId requestId,
|
||||||
bool isQos,
|
bool isQos,
|
||||||
QoSOptions qos);
|
IQoSProvider qosProvider);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ using System.Net.Http.Headers;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Extensions.Primitives;
|
using Microsoft.Extensions.Primitives;
|
||||||
using Ocelot.Configuration;
|
using Ocelot.Requester.QoS;
|
||||||
|
|
||||||
namespace Ocelot.Request.Builder
|
namespace Ocelot.Request.Builder
|
||||||
{
|
{
|
||||||
@ -24,7 +24,7 @@ namespace Ocelot.Request.Builder
|
|||||||
private IRequestCookieCollection _cookies;
|
private IRequestCookieCollection _cookies;
|
||||||
private readonly string[] _unsupportedHeaders = {"host"};
|
private readonly string[] _unsupportedHeaders = {"host"};
|
||||||
private bool _isQos;
|
private bool _isQos;
|
||||||
private QoSOptions _qos;
|
private IQoSProvider _qoSProvider;
|
||||||
|
|
||||||
public RequestBuilder WithHttpMethod(string httpMethod)
|
public RequestBuilder WithHttpMethod(string httpMethod)
|
||||||
{
|
{
|
||||||
@ -80,9 +80,9 @@ namespace Ocelot.Request.Builder
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RequestBuilder WithQos(QoSOptions qos)
|
public RequestBuilder WithQos(IQoSProvider qoSProvider)
|
||||||
{
|
{
|
||||||
_qos = qos;
|
_qoSProvider = qoSProvider;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,7 +105,7 @@ namespace Ocelot.Request.Builder
|
|||||||
|
|
||||||
var cookieContainer = CreateCookieContainer(uri);
|
var cookieContainer = CreateCookieContainer(uri);
|
||||||
|
|
||||||
return new Request(httpRequestMessage, cookieContainer,_isQos,_qos);
|
return new Request(httpRequestMessage, cookieContainer,_isQos, _qoSProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Uri CreateUri()
|
private Uri CreateUri()
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Ocelot.Infrastructure.RequestData;
|
using Ocelot.Infrastructure.RequestData;
|
||||||
using Ocelot.Logging;
|
using Ocelot.Logging;
|
||||||
using Ocelot.Middleware;
|
using Ocelot.Middleware;
|
||||||
using Ocelot.Request.Builder;
|
using Ocelot.Request.Builder;
|
||||||
|
using Ocelot.Requester.QoS;
|
||||||
|
|
||||||
namespace Ocelot.Request.Middleware
|
namespace Ocelot.Request.Middleware
|
||||||
{
|
{
|
||||||
@ -13,15 +13,18 @@ namespace Ocelot.Request.Middleware
|
|||||||
private readonly RequestDelegate _next;
|
private readonly RequestDelegate _next;
|
||||||
private readonly IRequestCreator _requestCreator;
|
private readonly IRequestCreator _requestCreator;
|
||||||
private readonly IOcelotLogger _logger;
|
private readonly IOcelotLogger _logger;
|
||||||
|
private readonly IQosProviderHouse _qosProviderHouse;
|
||||||
|
|
||||||
public HttpRequestBuilderMiddleware(RequestDelegate next,
|
public HttpRequestBuilderMiddleware(RequestDelegate next,
|
||||||
IOcelotLoggerFactory loggerFactory,
|
IOcelotLoggerFactory loggerFactory,
|
||||||
IRequestScopedDataRepository requestScopedDataRepository,
|
IRequestScopedDataRepository requestScopedDataRepository,
|
||||||
IRequestCreator requestCreator)
|
IRequestCreator requestCreator,
|
||||||
|
IQosProviderHouse qosProviderHouse)
|
||||||
:base(requestScopedDataRepository)
|
:base(requestScopedDataRepository)
|
||||||
{
|
{
|
||||||
_next = next;
|
_next = next;
|
||||||
_requestCreator = requestCreator;
|
_requestCreator = requestCreator;
|
||||||
|
_qosProviderHouse = qosProviderHouse;
|
||||||
_logger = loggerFactory.CreateLogger<HttpRequestBuilderMiddleware>();
|
_logger = loggerFactory.CreateLogger<HttpRequestBuilderMiddleware>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,17 +32,35 @@ namespace Ocelot.Request.Middleware
|
|||||||
{
|
{
|
||||||
_logger.LogDebug("started calling request builder middleware");
|
_logger.LogDebug("started calling request builder middleware");
|
||||||
|
|
||||||
|
var qosProvider = _qosProviderHouse.Get(DownstreamRoute.ReRoute.ReRouteKey);
|
||||||
|
|
||||||
|
if (qosProvider.IsError)
|
||||||
|
{
|
||||||
|
_logger.LogDebug("IQosProviderHouse returned an error, setting pipeline error");
|
||||||
|
|
||||||
|
SetPipelineError(qosProvider.Errors);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var buildResult = await _requestCreator
|
var buildResult = await _requestCreator
|
||||||
.Build(context.Request.Method, DownstreamUrl, context.Request.Body,
|
.Build(context.Request.Method,
|
||||||
context.Request.Headers, context.Request.Cookies, context.Request.QueryString,
|
DownstreamUrl,
|
||||||
context.Request.ContentType, new RequestId.RequestId(DownstreamRoute?.ReRoute?.RequestIdKey, context.TraceIdentifier),
|
context.Request.Body,
|
||||||
DownstreamRoute.ReRoute.IsQos,DownstreamRoute.ReRoute.QosOptions);
|
context.Request.Headers,
|
||||||
|
context.Request.Cookies,
|
||||||
|
context.Request.QueryString,
|
||||||
|
context.Request.ContentType,
|
||||||
|
new RequestId.RequestId(DownstreamRoute?.ReRoute?.RequestIdKey, context.TraceIdentifier),
|
||||||
|
DownstreamRoute.ReRoute.IsQos,
|
||||||
|
qosProvider.Data);
|
||||||
|
|
||||||
if (buildResult.IsError)
|
if (buildResult.IsError)
|
||||||
{
|
{
|
||||||
_logger.LogDebug("IRequestCreator returned an error, setting pipeline error");
|
_logger.LogDebug("IRequestCreator returned an error, setting pipeline error");
|
||||||
|
|
||||||
SetPipelineError(buildResult.Errors);
|
SetPipelineError(buildResult.Errors);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_logger.LogDebug("setting upstream request");
|
_logger.LogDebug("setting upstream request");
|
||||||
|
@ -2,22 +2,27 @@
|
|||||||
using Ocelot.Values;
|
using Ocelot.Values;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
using Ocelot.Requester.QoS;
|
||||||
|
|
||||||
namespace Ocelot.Request
|
namespace Ocelot.Request
|
||||||
{
|
{
|
||||||
public class Request
|
public class Request
|
||||||
{
|
{
|
||||||
public Request(HttpRequestMessage httpRequestMessage, CookieContainer cookieContainer,bool isQos, QoSOptions qos)
|
public Request(
|
||||||
|
HttpRequestMessage httpRequestMessage,
|
||||||
|
CookieContainer cookieContainer,
|
||||||
|
bool isQos,
|
||||||
|
IQoSProvider qosProvider)
|
||||||
{
|
{
|
||||||
HttpRequestMessage = httpRequestMessage;
|
HttpRequestMessage = httpRequestMessage;
|
||||||
CookieContainer = cookieContainer;
|
CookieContainer = cookieContainer;
|
||||||
IsQos = isQos;
|
IsQos = isQos;
|
||||||
Qos = qos;
|
QosProvider = qosProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
public HttpRequestMessage HttpRequestMessage { get; private set; }
|
public HttpRequestMessage HttpRequestMessage { get; private set; }
|
||||||
public CookieContainer CookieContainer { get; private set; }
|
public CookieContainer CookieContainer { get; private set; }
|
||||||
public bool IsQos { get; private set; }
|
public bool IsQos { get; private set; }
|
||||||
public QoSOptions Qos { get; private set; }
|
public IQoSProvider QosProvider { get; private set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,74 +0,0 @@
|
|||||||
using Ocelot.Logging;
|
|
||||||
using Polly;
|
|
||||||
using Polly.CircuitBreaker;
|
|
||||||
using Polly.Timeout;
|
|
||||||
using System;
|
|
||||||
using System.Net;
|
|
||||||
using System.Net.Http;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Ocelot.Requester
|
|
||||||
{
|
|
||||||
public class CircuitBreakingDelegatingHandler : DelegatingHandler
|
|
||||||
{
|
|
||||||
private readonly IOcelotLogger _logger;
|
|
||||||
private readonly int _exceptionsAllowedBeforeBreaking;
|
|
||||||
private readonly TimeSpan _durationOfBreak;
|
|
||||||
private readonly Policy _circuitBreakerPolicy;
|
|
||||||
private readonly TimeoutPolicy _timeoutPolicy;
|
|
||||||
|
|
||||||
public CircuitBreakingDelegatingHandler(int exceptionsAllowedBeforeBreaking, TimeSpan durationOfBreak,TimeSpan timeoutValue
|
|
||||||
,TimeoutStrategy timeoutStrategy, IOcelotLogger logger, HttpMessageHandler innerHandler)
|
|
||||||
: base(innerHandler)
|
|
||||||
{
|
|
||||||
this._exceptionsAllowedBeforeBreaking = exceptionsAllowedBeforeBreaking;
|
|
||||||
this._durationOfBreak = durationOfBreak;
|
|
||||||
|
|
||||||
_circuitBreakerPolicy = Policy
|
|
||||||
.Handle<HttpRequestException>()
|
|
||||||
.Or<TimeoutRejectedException>()
|
|
||||||
.Or<TimeoutException>()
|
|
||||||
.CircuitBreakerAsync(
|
|
||||||
exceptionsAllowedBeforeBreaking: exceptionsAllowedBeforeBreaking,
|
|
||||||
durationOfBreak: durationOfBreak,
|
|
||||||
onBreak: (ex, breakDelay) =>
|
|
||||||
{
|
|
||||||
_logger.LogError(".Breaker logging: Breaking the circuit for " + breakDelay.TotalMilliseconds + "ms!", ex);
|
|
||||||
},
|
|
||||||
onReset: () => _logger.LogDebug(".Breaker logging: Call ok! Closed the circuit again."),
|
|
||||||
onHalfOpen: () => _logger.LogDebug(".Breaker logging: Half-open; next call is a trial.")
|
|
||||||
);
|
|
||||||
_timeoutPolicy = Policy.TimeoutAsync(timeoutValue, timeoutStrategy);
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
Task<HttpResponseMessage> responseTask = null;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
responseTask = Policy.WrapAsync(_circuitBreakerPolicy, _timeoutPolicy).ExecuteAsync<HttpResponseMessage>(() =>
|
|
||||||
{
|
|
||||||
return base.SendAsync(request,cancellationToken);
|
|
||||||
});
|
|
||||||
return responseTask;
|
|
||||||
}
|
|
||||||
catch (BrokenCircuitException ex)
|
|
||||||
{
|
|
||||||
_logger.LogError($"Reached to allowed number of exceptions. Circuit is open. AllowedExceptionCount: {_exceptionsAllowedBeforeBreaking}, DurationOfBreak: {_durationOfBreak}",ex);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
catch (HttpRequestException)
|
|
||||||
{
|
|
||||||
return responseTask;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsTransientFailure(HttpResponseMessage result)
|
|
||||||
{
|
|
||||||
return result.StatusCode >= HttpStatusCode.InternalServerError;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,35 +1,39 @@
|
|||||||
using Ocelot.Configuration;
|
using System;
|
||||||
using Ocelot.Logging;
|
|
||||||
using Ocelot.Values;
|
|
||||||
using Polly.Timeout;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using Ocelot.Logging;
|
||||||
|
using Ocelot.Requester.QoS;
|
||||||
|
|
||||||
namespace Ocelot.Requester
|
namespace Ocelot.Requester
|
||||||
{
|
{
|
||||||
internal class HttpClientBuilder
|
internal class HttpClientBuilder
|
||||||
{
|
{
|
||||||
private readonly Dictionary<int, Func<DelegatingHandler>> handlers = new Dictionary<int, Func<DelegatingHandler>>();
|
private readonly Dictionary<int, Func<DelegatingHandler>> _handlers = new Dictionary<int, Func<DelegatingHandler>>();
|
||||||
|
|
||||||
public HttpClientBuilder WithCircuitBreaker(QoSOptions qos, IOcelotLogger logger, HttpMessageHandler innerHandler)
|
public HttpClientBuilder WithQoS(IQoSProvider qoSProvider, IOcelotLogger logger, HttpMessageHandler innerHandler)
|
||||||
{
|
{
|
||||||
handlers.Add(5000, () => new CircuitBreakingDelegatingHandler(qos.ExceptionsAllowedBeforeBreaking, qos.DurationOfBreak, qos.TimeoutValue, qos.TimeoutStrategy, logger, innerHandler));
|
_handlers.Add(5000, () => new PollyCircuitBreakingDelegatingHandler(qoSProvider, logger, innerHandler));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal HttpClient Build(HttpMessageHandler innerHandler)
|
internal HttpClient Build(HttpMessageHandler innerHandler)
|
||||||
{
|
{
|
||||||
return handlers.Any() ? new HttpClient(CreateHttpMessageHandler()) : new HttpClient(innerHandler);
|
return _handlers.Any() ?
|
||||||
|
new HttpClient(CreateHttpMessageHandler()) :
|
||||||
|
new HttpClient(innerHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
private HttpMessageHandler CreateHttpMessageHandler()
|
private HttpMessageHandler CreateHttpMessageHandler()
|
||||||
{
|
{
|
||||||
HttpMessageHandler httpMessageHandler = new HttpClientHandler();
|
HttpMessageHandler httpMessageHandler = new HttpClientHandler();
|
||||||
|
|
||||||
handlers.OrderByDescending(handler => handler.Key).Select(handler => handler.Value).Reverse().ToList().ForEach(handler =>
|
_handlers
|
||||||
|
.OrderByDescending(handler => handler.Key)
|
||||||
|
.Select(handler => handler.Value)
|
||||||
|
.Reverse()
|
||||||
|
.ToList()
|
||||||
|
.ForEach(handler =>
|
||||||
{
|
{
|
||||||
var delegatingHandler = handler();
|
var delegatingHandler = handler();
|
||||||
delegatingHandler.InnerHandler = httpMessageHandler;
|
delegatingHandler.InnerHandler = httpMessageHandler;
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Ocelot.Errors;
|
|
||||||
using Ocelot.Responses;
|
|
||||||
using Ocelot.Logging;
|
using Ocelot.Logging;
|
||||||
|
using Ocelot.Responses;
|
||||||
|
using Polly.CircuitBreaker;
|
||||||
|
using Polly.Timeout;
|
||||||
|
|
||||||
namespace Ocelot.Requester
|
namespace Ocelot.Requester
|
||||||
{
|
{
|
||||||
@ -19,14 +19,15 @@ namespace Ocelot.Requester
|
|||||||
|
|
||||||
public async Task<Response<HttpResponseMessage>> GetResponse(Request.Request request)
|
public async Task<Response<HttpResponseMessage>> GetResponse(Request.Request request)
|
||||||
{
|
{
|
||||||
HttpClientBuilder builder = new HttpClientBuilder();
|
var builder = new HttpClientBuilder();
|
||||||
|
|
||||||
using (var handler = new HttpClientHandler { CookieContainer = request.CookieContainer })
|
using (var handler = new HttpClientHandler { CookieContainer = request.CookieContainer })
|
||||||
{
|
{
|
||||||
if (request.IsQos)
|
if (request.IsQos)
|
||||||
{
|
{
|
||||||
builder.WithCircuitBreaker(request.Qos, _logger, handler);
|
builder.WithQoS(request.QosProvider, _logger, handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
using (var httpClient = builder.Build(handler))
|
using (var httpClient = builder.Build(handler))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@ -34,13 +35,19 @@ namespace Ocelot.Requester
|
|||||||
var response = await httpClient.SendAsync(request.HttpRequestMessage);
|
var response = await httpClient.SendAsync(request.HttpRequestMessage);
|
||||||
return new OkResponse<HttpResponseMessage>(response);
|
return new OkResponse<HttpResponseMessage>(response);
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (TimeoutRejectedException exception)
|
||||||
{
|
{
|
||||||
return
|
return
|
||||||
new ErrorResponse<HttpResponseMessage>(new List<Error>
|
new ErrorResponse<HttpResponseMessage>(new RequestTimedOutError(exception));
|
||||||
|
}
|
||||||
|
catch (BrokenCircuitException exception)
|
||||||
{
|
{
|
||||||
new UnableToCompleteRequestError(exception)
|
return
|
||||||
});
|
new ErrorResponse<HttpResponseMessage>(new RequestTimedOutError(exception));
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
return new ErrorResponse<HttpResponseMessage>(new UnableToCompleteRequestError(exception));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,47 @@
|
|||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Ocelot.Logging;
|
||||||
|
using Ocelot.Requester.QoS;
|
||||||
|
using Polly;
|
||||||
|
using Polly.CircuitBreaker;
|
||||||
|
using Polly.Timeout;
|
||||||
|
|
||||||
|
namespace Ocelot.Requester
|
||||||
|
{
|
||||||
|
public class PollyCircuitBreakingDelegatingHandler : DelegatingHandler
|
||||||
|
{
|
||||||
|
private readonly IQoSProvider _qoSProvider;
|
||||||
|
private readonly IOcelotLogger _logger;
|
||||||
|
|
||||||
|
public PollyCircuitBreakingDelegatingHandler(
|
||||||
|
IQoSProvider qoSProvider,
|
||||||
|
IOcelotLogger logger,
|
||||||
|
HttpMessageHandler innerHandler)
|
||||||
|
: base(innerHandler)
|
||||||
|
{
|
||||||
|
_qoSProvider = qoSProvider;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return await Policy
|
||||||
|
.WrapAsync(_qoSProvider.CircuitBreaker.CircuitBreakerPolicy, _qoSProvider.CircuitBreaker.TimeoutPolicy)
|
||||||
|
.ExecuteAsync(() => base.SendAsync(request,cancellationToken));
|
||||||
|
}
|
||||||
|
catch (BrokenCircuitException ex)
|
||||||
|
{
|
||||||
|
_logger.LogError($"Reached to allowed number of exceptions. Circuit is open",ex);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
catch (HttpRequestException ex)
|
||||||
|
{
|
||||||
|
_logger.LogError($"Error in CircuitBreakingDelegatingHandler.SendAync", ex);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
145
src/Ocelot/Requester/QoS/IQoSProviderFactory.cs
Normal file
145
src/Ocelot/Requester/QoS/IQoSProviderFactory.cs
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Net.Http;
|
||||||
|
using Ocelot.Configuration;
|
||||||
|
using Ocelot.LoadBalancer.LoadBalancers;
|
||||||
|
using Ocelot.Logging;
|
||||||
|
using Ocelot.Responses;
|
||||||
|
using Polly;
|
||||||
|
using Polly.CircuitBreaker;
|
||||||
|
using Polly.Timeout;
|
||||||
|
|
||||||
|
namespace Ocelot.Requester.QoS
|
||||||
|
{
|
||||||
|
public interface IQoSProviderFactory
|
||||||
|
{
|
||||||
|
IQoSProvider Get(ReRoute reRoute);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class QoSProviderFactory : IQoSProviderFactory
|
||||||
|
{
|
||||||
|
private readonly IOcelotLoggerFactory _loggerFactory;
|
||||||
|
|
||||||
|
public QoSProviderFactory(IOcelotLoggerFactory loggerFactory)
|
||||||
|
{
|
||||||
|
_loggerFactory = loggerFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IQoSProvider Get(ReRoute reRoute)
|
||||||
|
{
|
||||||
|
if (reRoute.IsQos)
|
||||||
|
{
|
||||||
|
return new PollyQoSProvider(reRoute, _loggerFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new NoQoSProvider();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IQoSProvider
|
||||||
|
{
|
||||||
|
CircuitBreaker CircuitBreaker { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class NoQoSProvider : IQoSProvider
|
||||||
|
{
|
||||||
|
public CircuitBreaker CircuitBreaker { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PollyQoSProvider : IQoSProvider
|
||||||
|
{
|
||||||
|
private readonly CircuitBreakerPolicy _circuitBreakerPolicy;
|
||||||
|
private readonly TimeoutPolicy _timeoutPolicy;
|
||||||
|
private readonly IOcelotLogger _logger;
|
||||||
|
private readonly CircuitBreaker _circuitBreaker;
|
||||||
|
|
||||||
|
public PollyQoSProvider(ReRoute reRoute, IOcelotLoggerFactory loggerFactory)
|
||||||
|
{
|
||||||
|
_logger = loggerFactory.CreateLogger<PollyQoSProvider>();
|
||||||
|
|
||||||
|
_timeoutPolicy = Policy.TimeoutAsync(reRoute.QosOptions.TimeoutValue, reRoute.QosOptions.TimeoutStrategy);
|
||||||
|
|
||||||
|
_circuitBreakerPolicy = Policy
|
||||||
|
.Handle<HttpRequestException>()
|
||||||
|
.Or<TimeoutRejectedException>()
|
||||||
|
.Or<TimeoutException>()
|
||||||
|
.CircuitBreakerAsync(
|
||||||
|
exceptionsAllowedBeforeBreaking: reRoute.QosOptions.ExceptionsAllowedBeforeBreaking,
|
||||||
|
durationOfBreak: reRoute.QosOptions.DurationOfBreak,
|
||||||
|
onBreak: (ex, breakDelay) =>
|
||||||
|
{
|
||||||
|
_logger.LogError(
|
||||||
|
".Breaker logging: Breaking the circuit for " + breakDelay.TotalMilliseconds + "ms!", ex);
|
||||||
|
},
|
||||||
|
onReset: () =>
|
||||||
|
{
|
||||||
|
_logger.LogDebug(".Breaker logging: Call ok! Closed the circuit again.");
|
||||||
|
},
|
||||||
|
onHalfOpen: () =>
|
||||||
|
{
|
||||||
|
_logger.LogDebug(".Breaker logging: Half-open; next call is a trial.");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
_circuitBreaker = new CircuitBreaker(_circuitBreakerPolicy, _timeoutPolicy);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CircuitBreaker CircuitBreaker => _circuitBreaker;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CircuitBreaker
|
||||||
|
{
|
||||||
|
public CircuitBreaker(CircuitBreakerPolicy circuitBreakerPolicy, TimeoutPolicy timeoutPolicy)
|
||||||
|
{
|
||||||
|
CircuitBreakerPolicy = circuitBreakerPolicy;
|
||||||
|
TimeoutPolicy = timeoutPolicy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CircuitBreakerPolicy CircuitBreakerPolicy { get; private set; }
|
||||||
|
public TimeoutPolicy TimeoutPolicy { get; private set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public interface IQosProviderHouse
|
||||||
|
{
|
||||||
|
Response<IQoSProvider> Get(string key);
|
||||||
|
Response Add(string key, IQoSProvider loadBalancer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class QosProviderHouse : IQosProviderHouse
|
||||||
|
{
|
||||||
|
private readonly Dictionary<string, IQoSProvider> _qoSProviders;
|
||||||
|
|
||||||
|
public QosProviderHouse()
|
||||||
|
{
|
||||||
|
_qoSProviders = new Dictionary<string, IQoSProvider>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Response<IQoSProvider> Get(string key)
|
||||||
|
{
|
||||||
|
IQoSProvider qoSProvider;
|
||||||
|
|
||||||
|
if (_qoSProviders.TryGetValue(key, out qoSProvider))
|
||||||
|
{
|
||||||
|
return new OkResponse<IQoSProvider>(_qoSProviders[key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ErrorResponse<IQoSProvider>(new List<Ocelot.Errors.Error>()
|
||||||
|
{
|
||||||
|
new UnableToFindQoSProviderError($"unabe to find qos provider for {key}")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public Response Add(string key, IQoSProvider loadBalancer)
|
||||||
|
{
|
||||||
|
if (!_qoSProviders.ContainsKey(key))
|
||||||
|
{
|
||||||
|
_qoSProviders.Add(key, loadBalancer);
|
||||||
|
}
|
||||||
|
|
||||||
|
_qoSProviders.Remove(key);
|
||||||
|
_qoSProviders.Add(key, loadBalancer);
|
||||||
|
return new OkResponse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
16
src/Ocelot/Requester/QoS/UnableToFindQoSProviderError.cs
Normal file
16
src/Ocelot/Requester/QoS/UnableToFindQoSProviderError.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Ocelot.Errors;
|
||||||
|
|
||||||
|
namespace Ocelot.Requester.QoS
|
||||||
|
{
|
||||||
|
public class UnableToFindQoSProviderError : Error
|
||||||
|
{
|
||||||
|
public UnableToFindQoSProviderError(string message)
|
||||||
|
: base(message, OcelotErrorCode.UnableToFindQoSProviderError)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
13
src/Ocelot/Requester/RequestTimedOutError.cs
Normal file
13
src/Ocelot/Requester/RequestTimedOutError.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
using Ocelot.Errors;
|
||||||
|
|
||||||
|
namespace Ocelot.Requester
|
||||||
|
{
|
||||||
|
public class RequestTimedOutError : Error
|
||||||
|
{
|
||||||
|
public RequestTimedOutError(Exception exception)
|
||||||
|
: base($"Timeout making http request, exception: {exception.Message}", OcelotErrorCode.RequestTimedOutError)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -22,6 +22,11 @@ namespace Ocelot.Responder
|
|||||||
return new OkResponse<int>(403);
|
return new OkResponse<int>(403);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (errors.Any(e => e.Code == OcelotErrorCode.RequestTimedOutError))
|
||||||
|
{
|
||||||
|
return new OkResponse<int>(503);
|
||||||
|
}
|
||||||
|
|
||||||
return new OkResponse<int>(404);
|
return new OkResponse<int>(404);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,13 @@ namespace Ocelot.Responses
|
|||||||
{
|
{
|
||||||
public class ErrorResponse<T> : Response<T>
|
public class ErrorResponse<T> : Response<T>
|
||||||
{
|
{
|
||||||
public ErrorResponse(List<Error> errors) : base(errors)
|
public ErrorResponse(Error error)
|
||||||
|
: base(new List<Error> {error})
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
public ErrorResponse(List<Error> errors)
|
||||||
|
: base(errors)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
namespace Ocelot.Values
|
namespace Ocelot.Values
|
||||||
{
|
{
|
||||||
public class DownstreamPathTemplate
|
public class PathTemplate
|
||||||
{
|
{
|
||||||
public DownstreamPathTemplate(string value)
|
public PathTemplate(string value)
|
||||||
{
|
{
|
||||||
Value = value;
|
Value = value;
|
||||||
}
|
}
|
||||||
|
BIN
test/.DS_Store
vendored
BIN
test/.DS_Store
vendored
Binary file not shown.
@ -47,7 +47,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
DownstreamPort = _downstreamServicePort,
|
DownstreamPort = _downstreamServicePort,
|
||||||
DownstreamHost = _downstreamServiceHost,
|
DownstreamHost = _downstreamServiceHost,
|
||||||
DownstreamScheme = _downstreamServiceScheme,
|
DownstreamScheme = _downstreamServiceScheme,
|
||||||
UpstreamTemplate = "/",
|
UpstreamPathTemplate = "/",
|
||||||
UpstreamHttpMethod = "Post",
|
UpstreamHttpMethod = "Post",
|
||||||
AuthenticationOptions = new FileAuthenticationOptions
|
AuthenticationOptions = new FileAuthenticationOptions
|
||||||
{
|
{
|
||||||
@ -85,7 +85,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
DownstreamPort = _downstreamServicePort,
|
DownstreamPort = _downstreamServicePort,
|
||||||
DownstreamHost = _downstreamServiceHost,
|
DownstreamHost = _downstreamServiceHost,
|
||||||
DownstreamScheme = _downstreamServiceScheme,
|
DownstreamScheme = _downstreamServiceScheme,
|
||||||
UpstreamTemplate = "/",
|
UpstreamPathTemplate = "/",
|
||||||
UpstreamHttpMethod = "Post",
|
UpstreamHttpMethod = "Post",
|
||||||
AuthenticationOptions = new FileAuthenticationOptions
|
AuthenticationOptions = new FileAuthenticationOptions
|
||||||
{
|
{
|
||||||
@ -123,7 +123,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
DownstreamPort = _downstreamServicePort,
|
DownstreamPort = _downstreamServicePort,
|
||||||
DownstreamHost = _downstreamServiceHost,
|
DownstreamHost = _downstreamServiceHost,
|
||||||
DownstreamScheme = _downstreamServiceScheme,
|
DownstreamScheme = _downstreamServiceScheme,
|
||||||
UpstreamTemplate = "/",
|
UpstreamPathTemplate = "/",
|
||||||
UpstreamHttpMethod = "Get",
|
UpstreamHttpMethod = "Get",
|
||||||
AuthenticationOptions = new FileAuthenticationOptions
|
AuthenticationOptions = new FileAuthenticationOptions
|
||||||
{
|
{
|
||||||
@ -163,7 +163,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
DownstreamPort = _downstreamServicePort,
|
DownstreamPort = _downstreamServicePort,
|
||||||
DownstreamHost = _downstreamServiceHost,
|
DownstreamHost = _downstreamServiceHost,
|
||||||
DownstreamScheme = _downstreamServiceScheme,
|
DownstreamScheme = _downstreamServiceScheme,
|
||||||
UpstreamTemplate = "/",
|
UpstreamPathTemplate = "/",
|
||||||
UpstreamHttpMethod = "Post",
|
UpstreamHttpMethod = "Post",
|
||||||
|
|
||||||
AuthenticationOptions = new FileAuthenticationOptions
|
AuthenticationOptions = new FileAuthenticationOptions
|
||||||
@ -204,7 +204,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
DownstreamPort = _downstreamServicePort,
|
DownstreamPort = _downstreamServicePort,
|
||||||
DownstreamHost = _downstreamServiceHost,
|
DownstreamHost = _downstreamServiceHost,
|
||||||
DownstreamScheme = _downstreamServiceScheme,
|
DownstreamScheme = _downstreamServiceScheme,
|
||||||
UpstreamTemplate = "/",
|
UpstreamPathTemplate = "/",
|
||||||
UpstreamHttpMethod = "Post",
|
UpstreamHttpMethod = "Post",
|
||||||
AuthenticationOptions = new FileAuthenticationOptions
|
AuthenticationOptions = new FileAuthenticationOptions
|
||||||
{
|
{
|
||||||
|
@ -41,7 +41,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
DownstreamPort = 51876,
|
DownstreamPort = 51876,
|
||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
DownstreamHost = "localhost",
|
DownstreamHost = "localhost",
|
||||||
UpstreamTemplate = "/",
|
UpstreamPathTemplate = "/",
|
||||||
UpstreamHttpMethod = "Get",
|
UpstreamHttpMethod = "Get",
|
||||||
AuthenticationOptions = new FileAuthenticationOptions
|
AuthenticationOptions = new FileAuthenticationOptions
|
||||||
{
|
{
|
||||||
@ -98,7 +98,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
DownstreamPort = 51876,
|
DownstreamPort = 51876,
|
||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
DownstreamHost = "localhost",
|
DownstreamHost = "localhost",
|
||||||
UpstreamTemplate = "/",
|
UpstreamPathTemplate = "/",
|
||||||
UpstreamHttpMethod = "Get",
|
UpstreamHttpMethod = "Get",
|
||||||
AuthenticationOptions = new FileAuthenticationOptions
|
AuthenticationOptions = new FileAuthenticationOptions
|
||||||
{
|
{
|
||||||
|
@ -35,7 +35,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
DownstreamPort = 51879,
|
DownstreamPort = 51879,
|
||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
DownstreamHost = "localhost",
|
DownstreamHost = "localhost",
|
||||||
UpstreamTemplate = "/",
|
UpstreamPathTemplate = "/",
|
||||||
UpstreamHttpMethod = "Get",
|
UpstreamHttpMethod = "Get",
|
||||||
FileCacheOptions = new FileCacheOptions
|
FileCacheOptions = new FileCacheOptions
|
||||||
{
|
{
|
||||||
@ -71,7 +71,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
DownstreamPort = 51879,
|
DownstreamPort = 51879,
|
||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
DownstreamHost = "localhost",
|
DownstreamHost = "localhost",
|
||||||
UpstreamTemplate = "/",
|
UpstreamPathTemplate = "/",
|
||||||
UpstreamHttpMethod = "Get",
|
UpstreamHttpMethod = "Get",
|
||||||
FileCacheOptions = new FileCacheOptions
|
FileCacheOptions = new FileCacheOptions
|
||||||
{
|
{
|
||||||
|
@ -34,7 +34,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
DownstreamPort = 51879,
|
DownstreamPort = 51879,
|
||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
DownstreamHost = "localhost",
|
DownstreamHost = "localhost",
|
||||||
UpstreamTemplate = "/products/{productId}",
|
UpstreamPathTemplate = "/products/{productId}",
|
||||||
UpstreamHttpMethod = "Get",
|
UpstreamHttpMethod = "Get",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -61,7 +61,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
DownstreamPort = 51879,
|
DownstreamPort = 51879,
|
||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
DownstreamHost = "localhost",
|
DownstreamHost = "localhost",
|
||||||
UpstreamTemplate = "/products/{productId}",
|
UpstreamPathTemplate = "/products/{productId}",
|
||||||
UpstreamHttpMethod = "Get",
|
UpstreamHttpMethod = "Get",
|
||||||
ReRouteIsCaseSensitive = false,
|
ReRouteIsCaseSensitive = false,
|
||||||
}
|
}
|
||||||
@ -89,7 +89,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
DownstreamPort = 51879,
|
DownstreamPort = 51879,
|
||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
DownstreamHost = "localhost",
|
DownstreamHost = "localhost",
|
||||||
UpstreamTemplate = "/products/{productId}",
|
UpstreamPathTemplate = "/products/{productId}",
|
||||||
UpstreamHttpMethod = "Get",
|
UpstreamHttpMethod = "Get",
|
||||||
ReRouteIsCaseSensitive = true,
|
ReRouteIsCaseSensitive = true,
|
||||||
}
|
}
|
||||||
@ -117,7 +117,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
DownstreamPort = 51879,
|
DownstreamPort = 51879,
|
||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
DownstreamHost = "localhost",
|
DownstreamHost = "localhost",
|
||||||
UpstreamTemplate = "/PRODUCTS/{productId}",
|
UpstreamPathTemplate = "/PRODUCTS/{productId}",
|
||||||
UpstreamHttpMethod = "Get",
|
UpstreamHttpMethod = "Get",
|
||||||
ReRouteIsCaseSensitive = true,
|
ReRouteIsCaseSensitive = true,
|
||||||
}
|
}
|
||||||
@ -145,7 +145,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
DownstreamPort = 51879,
|
DownstreamPort = 51879,
|
||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
DownstreamHost = "localhost",
|
DownstreamHost = "localhost",
|
||||||
UpstreamTemplate = "/products/{productId}",
|
UpstreamPathTemplate = "/products/{productId}",
|
||||||
UpstreamHttpMethod = "Get",
|
UpstreamHttpMethod = "Get",
|
||||||
ReRouteIsCaseSensitive = true,
|
ReRouteIsCaseSensitive = true,
|
||||||
}
|
}
|
||||||
@ -173,7 +173,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
DownstreamPort = 51879,
|
DownstreamPort = 51879,
|
||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
DownstreamHost = "localhost",
|
DownstreamHost = "localhost",
|
||||||
UpstreamTemplate = "/PRODUCTS/{productId}",
|
UpstreamPathTemplate = "/PRODUCTS/{productId}",
|
||||||
UpstreamHttpMethod = "Get",
|
UpstreamHttpMethod = "Get",
|
||||||
ReRouteIsCaseSensitive = true,
|
ReRouteIsCaseSensitive = true,
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
DownstreamPort = 52876,
|
DownstreamPort = 52876,
|
||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
DownstreamHost = "localhost",
|
DownstreamHost = "localhost",
|
||||||
UpstreamTemplate = "/",
|
UpstreamPathTemplate = "/",
|
||||||
UpstreamHttpMethod = "Get",
|
UpstreamHttpMethod = "Get",
|
||||||
AuthenticationOptions = new FileAuthenticationOptions
|
AuthenticationOptions = new FileAuthenticationOptions
|
||||||
{
|
{
|
||||||
|
@ -55,7 +55,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
DownstreamPort = 57876,
|
DownstreamPort = 57876,
|
||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
DownstreamHost = "localhost",
|
DownstreamHost = "localhost",
|
||||||
UpstreamTemplate = "/",
|
UpstreamPathTemplate = "/",
|
||||||
UpstreamHttpMethod = "Get",
|
UpstreamHttpMethod = "Get",
|
||||||
AuthenticationOptions = new FileAuthenticationOptions
|
AuthenticationOptions = new FileAuthenticationOptions
|
||||||
{
|
{
|
||||||
|
@ -46,7 +46,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
DownstreamPort = 51879,
|
DownstreamPort = 51879,
|
||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
DownstreamHost = "localhost",
|
DownstreamHost = "localhost",
|
||||||
UpstreamTemplate = "/api/ClientRateLimit",
|
UpstreamPathTemplate = "/api/ClientRateLimit",
|
||||||
UpstreamHttpMethod = "Get",
|
UpstreamHttpMethod = "Get",
|
||||||
RequestIdKey = _steps.RequestIdKey,
|
RequestIdKey = _steps.RequestIdKey,
|
||||||
|
|
||||||
@ -56,7 +56,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
ClientWhitelist = new List<string>(),
|
ClientWhitelist = new List<string>(),
|
||||||
Limit = 3,
|
Limit = 3,
|
||||||
Period = "1s",
|
Period = "1s",
|
||||||
PeriodTimespan = 100
|
PeriodTimespan = 1000
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -101,7 +101,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
DownstreamPort = 51879,
|
DownstreamPort = 51879,
|
||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
DownstreamHost = "localhost",
|
DownstreamHost = "localhost",
|
||||||
UpstreamTemplate = "/api/ClientRateLimit",
|
UpstreamPathTemplate = "/api/ClientRateLimit",
|
||||||
UpstreamHttpMethod = "Get",
|
UpstreamHttpMethod = "Get",
|
||||||
RequestIdKey = _steps.RequestIdKey,
|
RequestIdKey = _steps.RequestIdKey,
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
DownstreamPort = 41879,
|
DownstreamPort = 41879,
|
||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
DownstreamHost = "localhost",
|
DownstreamHost = "localhost",
|
||||||
UpstreamTemplate = "/",
|
UpstreamPathTemplate = "/",
|
||||||
UpstreamHttpMethod = "Get",
|
UpstreamHttpMethod = "Get",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -86,7 +86,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
DownstreamPort = 41879,
|
DownstreamPort = 41879,
|
||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
DownstreamHost = "localhost",
|
DownstreamHost = "localhost",
|
||||||
UpstreamTemplate = "/",
|
UpstreamPathTemplate = "/",
|
||||||
UpstreamHttpMethod = "Get",
|
UpstreamHttpMethod = "Get",
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -124,7 +124,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
DownstreamPort = 41879,
|
DownstreamPort = 41879,
|
||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
DownstreamHost = "localhost",
|
DownstreamHost = "localhost",
|
||||||
UpstreamTemplate = "/",
|
UpstreamPathTemplate = "/",
|
||||||
UpstreamHttpMethod = "Get",
|
UpstreamHttpMethod = "Get",
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -162,7 +162,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
DownstreamPort = 41879,
|
DownstreamPort = 41879,
|
||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
DownstreamHost = "localhost",
|
DownstreamHost = "localhost",
|
||||||
UpstreamTemplate = "/",
|
UpstreamPathTemplate = "/",
|
||||||
UpstreamHttpMethod = "Get",
|
UpstreamHttpMethod = "Get",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -199,7 +199,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
DownstreamPort = 41879,
|
DownstreamPort = 41879,
|
||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
DownstreamHost = "localhost",
|
DownstreamHost = "localhost",
|
||||||
UpstreamTemplate = "/",
|
UpstreamPathTemplate = "/",
|
||||||
UpstreamHttpMethod = "Get",
|
UpstreamHttpMethod = "Get",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -236,7 +236,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
DownstreamPort = 41879,
|
DownstreamPort = 41879,
|
||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
DownstreamHost = "localhost",
|
DownstreamHost = "localhost",
|
||||||
UpstreamTemplate = "/",
|
UpstreamPathTemplate = "/",
|
||||||
UpstreamHttpMethod = "Get",
|
UpstreamHttpMethod = "Get",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
206
test/Ocelot.AcceptanceTests/QoSTests.cs
Normal file
206
test/Ocelot.AcceptanceTests/QoSTests.cs
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Ocelot.Configuration.File;
|
||||||
|
using TestStack.BDDfy;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Ocelot.AcceptanceTests
|
||||||
|
{
|
||||||
|
public class QoSTests : IDisposable
|
||||||
|
{
|
||||||
|
private IWebHost _brokenService;
|
||||||
|
private readonly Steps _steps;
|
||||||
|
private int _requestCount;
|
||||||
|
private IWebHost _workingService;
|
||||||
|
|
||||||
|
public QoSTests()
|
||||||
|
{
|
||||||
|
_steps = new Steps();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_open_circuit_breaker_then_close()
|
||||||
|
{
|
||||||
|
var configuration = new FileConfiguration
|
||||||
|
{
|
||||||
|
ReRoutes = new List<FileReRoute>
|
||||||
|
{
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamPathTemplate = "/",
|
||||||
|
DownstreamScheme = "http",
|
||||||
|
DownstreamHost = "localhost",
|
||||||
|
DownstreamPort = 51879,
|
||||||
|
UpstreamPathTemplate = "/",
|
||||||
|
UpstreamHttpMethod = "Get",
|
||||||
|
QoSOptions = new FileQoSOptions
|
||||||
|
{
|
||||||
|
ExceptionsAllowedBeforeBreaking = 1,
|
||||||
|
TimeoutValue = 500,
|
||||||
|
DurationOfBreak = 1000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.Given(x => x.GivenThereIsAPossiblyBrokenServiceRunningOn("http://localhost:51879", "Hello from Laura"))
|
||||||
|
.Given(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
|
.Given(x => _steps.GivenOcelotIsRunning())
|
||||||
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
|
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
||||||
|
.Given(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
|
.Given(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.ServiceUnavailable))
|
||||||
|
.Given(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
|
.Given(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.ServiceUnavailable))
|
||||||
|
.Given(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
|
.Given(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.ServiceUnavailable))
|
||||||
|
.Given(x => x.GivenIWaitMilliseconds(3000))
|
||||||
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
|
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void open_circuit_should_not_effect_different_reRoute()
|
||||||
|
{
|
||||||
|
var configuration = new FileConfiguration
|
||||||
|
{
|
||||||
|
ReRoutes = new List<FileReRoute>
|
||||||
|
{
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamPathTemplate = "/",
|
||||||
|
DownstreamScheme = "http",
|
||||||
|
DownstreamHost = "localhost",
|
||||||
|
DownstreamPort = 51879,
|
||||||
|
UpstreamPathTemplate = "/",
|
||||||
|
UpstreamHttpMethod = "Get",
|
||||||
|
QoSOptions = new FileQoSOptions
|
||||||
|
{
|
||||||
|
ExceptionsAllowedBeforeBreaking = 1,
|
||||||
|
TimeoutValue = 500,
|
||||||
|
DurationOfBreak = 1000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamPathTemplate = "/",
|
||||||
|
DownstreamScheme = "http",
|
||||||
|
DownstreamHost = "localhost",
|
||||||
|
DownstreamPort = 51880,
|
||||||
|
UpstreamPathTemplate = "working",
|
||||||
|
UpstreamHttpMethod = "Get",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.Given(x => x.GivenThereIsAPossiblyBrokenServiceRunningOn("http://localhost:51879", "Hello from Laura"))
|
||||||
|
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51880/", 200, "Hello from Tom"))
|
||||||
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
|
.And(x => _steps.GivenOcelotIsRunning())
|
||||||
|
.And(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
|
.And(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
|
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
||||||
|
.And(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
|
.And(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.ServiceUnavailable))
|
||||||
|
.And(x => _steps.WhenIGetUrlOnTheApiGateway("/working"))
|
||||||
|
.And(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
|
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Tom"))
|
||||||
|
.And(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
|
.And(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.ServiceUnavailable))
|
||||||
|
.And(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
|
.And(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.ServiceUnavailable))
|
||||||
|
.And(x => x.GivenIWaitMilliseconds(3000))
|
||||||
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
|
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenIWaitMilliseconds(int ms)
|
||||||
|
{
|
||||||
|
Thread.Sleep(ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenThereIsAPossiblyBrokenServiceRunningOn(string url, string responseBody)
|
||||||
|
{
|
||||||
|
_brokenService = new WebHostBuilder()
|
||||||
|
.UseUrls(url)
|
||||||
|
.UseKestrel()
|
||||||
|
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||||
|
.UseIISIntegration()
|
||||||
|
.UseUrls(url)
|
||||||
|
.Configure(app =>
|
||||||
|
{
|
||||||
|
app.Run(async context =>
|
||||||
|
{
|
||||||
|
//circuit starts closed
|
||||||
|
if (_requestCount == 0)
|
||||||
|
{
|
||||||
|
_requestCount++;
|
||||||
|
context.Response.StatusCode = 200;
|
||||||
|
await context.Response.WriteAsync(responseBody);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//request one times out and polly throws exception, circuit opens
|
||||||
|
if (_requestCount == 1)
|
||||||
|
{
|
||||||
|
_requestCount++;
|
||||||
|
await Task.Delay(1000);
|
||||||
|
context.Response.StatusCode = 200;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//after break closes we return 200 OK
|
||||||
|
if (_requestCount == 2)
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 200;
|
||||||
|
await context.Response.WriteAsync(responseBody);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
_brokenService.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenThereIsAServiceRunningOn(string url, int statusCode, string responseBody)
|
||||||
|
{
|
||||||
|
_workingService = new WebHostBuilder()
|
||||||
|
.UseUrls(url)
|
||||||
|
.UseKestrel()
|
||||||
|
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||||
|
.UseIISIntegration()
|
||||||
|
.UseUrls(url)
|
||||||
|
.Configure(app =>
|
||||||
|
{
|
||||||
|
app.Run(async context =>
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = statusCode;
|
||||||
|
await context.Response.WriteAsync(responseBody);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
_workingService.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_workingService?.Dispose();
|
||||||
|
_brokenService?.Dispose();
|
||||||
|
_steps.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -37,7 +37,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
DownstreamPort = 51879,
|
DownstreamPort = 51879,
|
||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
DownstreamHost = "localhost",
|
DownstreamHost = "localhost",
|
||||||
UpstreamTemplate = "/",
|
UpstreamPathTemplate = "/",
|
||||||
UpstreamHttpMethod = "Get",
|
UpstreamHttpMethod = "Get",
|
||||||
RequestIdKey = _steps.RequestIdKey,
|
RequestIdKey = _steps.RequestIdKey,
|
||||||
}
|
}
|
||||||
@ -65,7 +65,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
DownstreamPort = 51879,
|
DownstreamPort = 51879,
|
||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
DownstreamHost = "localhost",
|
DownstreamHost = "localhost",
|
||||||
UpstreamTemplate = "/",
|
UpstreamPathTemplate = "/",
|
||||||
UpstreamHttpMethod = "Get",
|
UpstreamHttpMethod = "Get",
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -95,8 +95,8 @@ namespace Ocelot.AcceptanceTests
|
|||||||
DownstreamPort = 51879,
|
DownstreamPort = 51879,
|
||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
DownstreamHost = "localhost",
|
DownstreamHost = "localhost",
|
||||||
UpstreamTemplate = "/",
|
UpstreamPathTemplate = "/",
|
||||||
UpstreamHttpMethod ="Get"
|
UpstreamHttpMethod = "Get",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
GlobalConfiguration = new FileGlobalConfiguration
|
GlobalConfiguration = new FileGlobalConfiguration
|
||||||
|
@ -30,7 +30,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
new FileReRoute
|
new FileReRoute
|
||||||
{
|
{
|
||||||
DownstreamPathTemplate = "http://localhost:53876/",
|
DownstreamPathTemplate = "http://localhost:53876/",
|
||||||
UpstreamTemplate = "/",
|
UpstreamPathTemplate = "/",
|
||||||
UpstreamHttpMethod = "Get"
|
UpstreamHttpMethod = "Get"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
DownstreamHost = "localhost",
|
DownstreamHost = "localhost",
|
||||||
DownstreamPort = 51879,
|
DownstreamPort = 51879,
|
||||||
UpstreamTemplate = "/",
|
UpstreamPathTemplate = "/",
|
||||||
UpstreamHttpMethod = "Get",
|
UpstreamHttpMethod = "Get",
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -73,7 +73,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
DownstreamHost = "localhost",
|
DownstreamHost = "localhost",
|
||||||
DownstreamPort = 51879,
|
DownstreamPort = 51879,
|
||||||
UpstreamTemplate = "/",
|
UpstreamPathTemplate = "/",
|
||||||
UpstreamHttpMethod = "Get",
|
UpstreamHttpMethod = "Get",
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -102,7 +102,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
DownstreamHost = "localhost/",
|
DownstreamHost = "localhost/",
|
||||||
DownstreamPort = 51879,
|
DownstreamPort = 51879,
|
||||||
UpstreamTemplate = "/",
|
UpstreamPathTemplate = "/",
|
||||||
UpstreamHttpMethod = "Get",
|
UpstreamHttpMethod = "Get",
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -131,7 +131,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
DownstreamHost = "localhost",
|
DownstreamHost = "localhost",
|
||||||
DownstreamPort = 51879,
|
DownstreamPort = 51879,
|
||||||
UpstreamTemplate = "/products/",
|
UpstreamPathTemplate = "/products/",
|
||||||
UpstreamHttpMethod = "Get",
|
UpstreamHttpMethod = "Get",
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -160,9 +160,8 @@ namespace Ocelot.AcceptanceTests
|
|||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
DownstreamHost = "localhost",
|
DownstreamHost = "localhost",
|
||||||
DownstreamPort = 51879,
|
DownstreamPort = 51879,
|
||||||
UpstreamTemplate = "/products",
|
UpstreamPathTemplate = "/products",
|
||||||
UpstreamHttpMethod = "Get",
|
UpstreamHttpMethod = "Get",
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -189,13 +188,14 @@ namespace Ocelot.AcceptanceTests
|
|||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
DownstreamHost = "localhost",
|
DownstreamHost = "localhost",
|
||||||
DownstreamPort = 51879,
|
DownstreamPort = 51879,
|
||||||
UpstreamTemplate = "/products/{productId}",
|
UpstreamPathTemplate = "/products/{productId}",
|
||||||
UpstreamHttpMethod = "Get",
|
UpstreamHttpMethod = "Get",
|
||||||
QoSOptions = new FileQoSOptions()
|
QoSOptions = new FileQoSOptions()
|
||||||
{
|
{
|
||||||
ExceptionsAllowedBeforeBreaking = 3,
|
ExceptionsAllowedBeforeBreaking = 3,
|
||||||
DurationOfBreak = 5,
|
DurationOfBreak = 5,
|
||||||
TimeoutValue = 5000 }
|
TimeoutValue = 5000
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -221,7 +221,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
DownstreamHost = "localhost",
|
DownstreamHost = "localhost",
|
||||||
DownstreamPort = 51879,
|
DownstreamPort = 51879,
|
||||||
UpstreamTemplate = "/products/{productId}",
|
UpstreamPathTemplate = "/products/{productId}",
|
||||||
UpstreamHttpMethod = "Get",
|
UpstreamHttpMethod = "Get",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -249,7 +249,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
DownstreamHost = "localhost",
|
DownstreamHost = "localhost",
|
||||||
DownstreamPort = 51879,
|
DownstreamPort = 51879,
|
||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
UpstreamTemplate = "/",
|
UpstreamPathTemplate = "/",
|
||||||
UpstreamHttpMethod = "Post",
|
UpstreamHttpMethod = "Post",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -274,7 +274,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
new FileReRoute
|
new FileReRoute
|
||||||
{
|
{
|
||||||
DownstreamPathTemplate = "/newThing",
|
DownstreamPathTemplate = "/newThing",
|
||||||
UpstreamTemplate = "/newThing",
|
UpstreamPathTemplate = "/newThing",
|
||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
DownstreamHost = "localhost",
|
DownstreamHost = "localhost",
|
||||||
DownstreamPort = 51879,
|
DownstreamPort = 51879,
|
||||||
|
@ -68,7 +68,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
{
|
{
|
||||||
DownstreamPathTemplate = "/",
|
DownstreamPathTemplate = "/",
|
||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
UpstreamTemplate = "/",
|
UpstreamPathTemplate = "/",
|
||||||
UpstreamHttpMethod = "Get",
|
UpstreamHttpMethod = "Get",
|
||||||
ServiceName = serviceName,
|
ServiceName = serviceName,
|
||||||
LoadBalancer = "LeastConnection",
|
LoadBalancer = "LeastConnection",
|
||||||
|
@ -6,6 +6,7 @@ using Moq;
|
|||||||
using Ocelot.Authentication.Handler;
|
using Ocelot.Authentication.Handler;
|
||||||
using Ocelot.Authentication.Handler.Creator;
|
using Ocelot.Authentication.Handler.Creator;
|
||||||
using Ocelot.Authentication.Handler.Factory;
|
using Ocelot.Authentication.Handler.Factory;
|
||||||
|
using Ocelot.Configuration.Builder;
|
||||||
using Ocelot.Errors;
|
using Ocelot.Errors;
|
||||||
using Ocelot.Responses;
|
using Ocelot.Responses;
|
||||||
using Shouldly;
|
using Shouldly;
|
||||||
@ -33,7 +34,11 @@ namespace Ocelot.UnitTests.Authentication
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void should_return_identity_server_access_token_handler()
|
public void should_return_identity_server_access_token_handler()
|
||||||
{
|
{
|
||||||
this.Given(x => x.GivenTheAuthenticationOptionsAre(new AuthenticationOptions("IdentityServer", "","",false, new List<string>(), "")))
|
var authenticationOptions = new AuthenticationOptionsBuilder()
|
||||||
|
.WithProvider("IdentityServer")
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
this.Given(x => x.GivenTheAuthenticationOptionsAre(authenticationOptions))
|
||||||
.And(x => x.GivenTheCreatorReturns())
|
.And(x => x.GivenTheCreatorReturns())
|
||||||
.When(x => x.WhenIGetFromTheFactory())
|
.When(x => x.WhenIGetFromTheFactory())
|
||||||
.Then(x => x.ThenTheHandlerIsReturned("IdentityServer"))
|
.Then(x => x.ThenTheHandlerIsReturned("IdentityServer"))
|
||||||
@ -43,7 +48,10 @@ namespace Ocelot.UnitTests.Authentication
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void should_return_error_if_cannot_create_handler()
|
public void should_return_error_if_cannot_create_handler()
|
||||||
{
|
{
|
||||||
this.Given(x => x.GivenTheAuthenticationOptionsAre(new AuthenticationOptions("IdentityServer", "", "", false, new List<string>(), "")))
|
var authenticationOptions = new AuthenticationOptionsBuilder()
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
this.Given(x => x.GivenTheAuthenticationOptionsAre(authenticationOptions))
|
||||||
.And(x => x.GivenTheCreatorReturnsAnError())
|
.And(x => x.GivenTheCreatorReturnsAnError())
|
||||||
.When(x => x.WhenIGetFromTheFactory())
|
.When(x => x.WhenIGetFromTheFactory())
|
||||||
.Then(x => x.ThenAnErrorResponseIsReturned())
|
.Then(x => x.ThenAnErrorResponseIsReturned())
|
||||||
@ -58,7 +66,7 @@ namespace Ocelot.UnitTests.Authentication
|
|||||||
private void GivenTheCreatorReturnsAnError()
|
private void GivenTheCreatorReturnsAnError()
|
||||||
{
|
{
|
||||||
_creator
|
_creator
|
||||||
.Setup(x => x.CreateIdentityServerAuthenticationHandler(It.IsAny<IApplicationBuilder>(), It.IsAny<AuthenticationOptions>()))
|
.Setup(x => x.Create(It.IsAny<IApplicationBuilder>(), It.IsAny<AuthenticationOptions>()))
|
||||||
.Returns(new ErrorResponse<RequestDelegate>(new List<Error>
|
.Returns(new ErrorResponse<RequestDelegate>(new List<Error>
|
||||||
{
|
{
|
||||||
new UnableToCreateAuthenticationHandlerError($"Unable to create authentication handler for xxx")
|
new UnableToCreateAuthenticationHandlerError($"Unable to create authentication handler for xxx")
|
||||||
@ -68,7 +76,7 @@ namespace Ocelot.UnitTests.Authentication
|
|||||||
private void GivenTheCreatorReturns()
|
private void GivenTheCreatorReturns()
|
||||||
{
|
{
|
||||||
_creator
|
_creator
|
||||||
.Setup(x => x.CreateIdentityServerAuthenticationHandler(It.IsAny<IApplicationBuilder>(), It.IsAny<AuthenticationOptions>()))
|
.Setup(x => x.Create(It.IsAny<IApplicationBuilder>(), It.IsAny<AuthenticationOptions>()))
|
||||||
.Returns(new OkResponse<RequestDelegate>(x => Task.CompletedTask));
|
.Returns(new OkResponse<RequestDelegate>(x => Task.CompletedTask));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,7 +71,9 @@ namespace Ocelot.UnitTests.Authentication
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void should_call_next_middleware_if_route_is_not_authenticated()
|
public void should_call_next_middleware_if_route_is_not_authenticated()
|
||||||
{
|
{
|
||||||
this.Given(x => x.GivenTheDownStreamRouteIs(new DownstreamRoute(new List<UrlPathPlaceholderNameAndValue>(), new ReRouteBuilder().Build())))
|
this.Given(x => x.GivenTheDownStreamRouteIs(new DownstreamRoute(new List<UrlPathPlaceholderNameAndValue>(), new ReRouteBuilder()
|
||||||
|
.WithUpstreamHttpMethod("Get")
|
||||||
|
.Build())))
|
||||||
.When(x => x.WhenICallTheMiddleware())
|
.When(x => x.WhenICallTheMiddleware())
|
||||||
.Then(x => x.ThenTheUserIsAuthenticated())
|
.Then(x => x.ThenTheUserIsAuthenticated())
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
|
@ -63,7 +63,11 @@ namespace Ocelot.UnitTests.Authorization
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void should_call_authorisation_service()
|
public void should_call_authorisation_service()
|
||||||
{
|
{
|
||||||
this.Given(x => x.GivenTheDownStreamRouteIs(new DownstreamRoute(new List<UrlPathPlaceholderNameAndValue>(), new ReRouteBuilder().WithIsAuthorised(true).Build())))
|
this.Given(x => x.GivenTheDownStreamRouteIs(new DownstreamRoute(new List<UrlPathPlaceholderNameAndValue>(),
|
||||||
|
new ReRouteBuilder()
|
||||||
|
.WithIsAuthorised(true)
|
||||||
|
.WithUpstreamHttpMethod("Get") .WithUpstreamHttpMethod("Get")
|
||||||
|
.Build())))
|
||||||
.And(x => x.GivenTheAuthServiceReturns(new OkResponse<bool>(true)))
|
.And(x => x.GivenTheAuthServiceReturns(new OkResponse<bool>(true)))
|
||||||
.When(x => x.WhenICallTheMiddleware())
|
.When(x => x.WhenICallTheMiddleware())
|
||||||
.Then(x => x.ThenTheAuthServiceIsCalledCorrectly())
|
.Then(x => x.ThenTheAuthServiceIsCalledCorrectly())
|
||||||
|
@ -87,7 +87,12 @@ namespace Ocelot.UnitTests.Cache
|
|||||||
|
|
||||||
private void GivenTheDownstreamRouteIs()
|
private void GivenTheDownstreamRouteIs()
|
||||||
{
|
{
|
||||||
var reRoute = new ReRouteBuilder().WithIsCached(true).WithCacheOptions(new CacheOptions(100)).Build();
|
var reRoute = new ReRouteBuilder()
|
||||||
|
.WithIsCached(true)
|
||||||
|
.WithCacheOptions(new CacheOptions(100))
|
||||||
|
.WithUpstreamHttpMethod("Get")
|
||||||
|
.Build();
|
||||||
|
|
||||||
var downstreamRoute = new DownstreamRoute(new List<UrlPathPlaceholderNameAndValue>(), reRoute);
|
var downstreamRoute = new DownstreamRoute(new List<UrlPathPlaceholderNameAndValue>(), reRoute);
|
||||||
|
|
||||||
_scopedRepo
|
_scopedRepo
|
||||||
|
@ -72,6 +72,7 @@ namespace Ocelot.UnitTests.Claims
|
|||||||
{
|
{
|
||||||
new ClaimToThing("sub", "UserType", "|", 0)
|
new ClaimToThing("sub", "UserType", "|", 0)
|
||||||
})
|
})
|
||||||
|
.WithUpstreamHttpMethod("Get")
|
||||||
.Build());
|
.Build());
|
||||||
|
|
||||||
this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute))
|
this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute))
|
||||||
|
@ -29,7 +29,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
new FileReRoute
|
new FileReRoute
|
||||||
{
|
{
|
||||||
DownstreamPathTemplate = "http://www.bbc.co.uk/api/products/{productId}",
|
DownstreamPathTemplate = "http://www.bbc.co.uk/api/products/{productId}",
|
||||||
UpstreamTemplate = "http://asdf.com"
|
UpstreamPathTemplate = "http://asdf.com"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
@ -48,7 +48,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
new FileReRoute
|
new FileReRoute
|
||||||
{
|
{
|
||||||
DownstreamPathTemplate = "/api/products/",
|
DownstreamPathTemplate = "/api/products/",
|
||||||
UpstreamTemplate = "http://asdf.com"
|
UpstreamPathTemplate = "http://asdf.com"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
@ -67,7 +67,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
new FileReRoute
|
new FileReRoute
|
||||||
{
|
{
|
||||||
DownstreamPathTemplate = "/api/products/",
|
DownstreamPathTemplate = "/api/products/",
|
||||||
UpstreamTemplate = "http://asdf.com",
|
UpstreamPathTemplate = "http://asdf.com",
|
||||||
AuthenticationOptions = new FileAuthenticationOptions
|
AuthenticationOptions = new FileAuthenticationOptions
|
||||||
{
|
{
|
||||||
Provider = "IdentityServer"
|
Provider = "IdentityServer"
|
||||||
@ -90,7 +90,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
new FileReRoute
|
new FileReRoute
|
||||||
{
|
{
|
||||||
DownstreamPathTemplate = "/api/products/",
|
DownstreamPathTemplate = "/api/products/",
|
||||||
UpstreamTemplate = "http://asdf.com",
|
UpstreamPathTemplate = "http://asdf.com",
|
||||||
AuthenticationOptions = new FileAuthenticationOptions
|
AuthenticationOptions = new FileAuthenticationOptions
|
||||||
{
|
{
|
||||||
Provider = "BootyBootyBottyRockinEverywhere"
|
Provider = "BootyBootyBottyRockinEverywhere"
|
||||||
@ -114,12 +114,12 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
new FileReRoute
|
new FileReRoute
|
||||||
{
|
{
|
||||||
DownstreamPathTemplate = "/api/products/",
|
DownstreamPathTemplate = "/api/products/",
|
||||||
UpstreamTemplate = "http://asdf.com"
|
UpstreamPathTemplate = "http://asdf.com"
|
||||||
},
|
},
|
||||||
new FileReRoute
|
new FileReRoute
|
||||||
{
|
{
|
||||||
DownstreamPathTemplate = "http://www.bbc.co.uk",
|
DownstreamPathTemplate = "http://www.bbc.co.uk",
|
||||||
UpstreamTemplate = "http://asdf.com"
|
UpstreamPathTemplate = "http://asdf.com"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
@ -9,6 +9,7 @@ using Ocelot.Configuration.File;
|
|||||||
using Ocelot.Configuration.Parser;
|
using Ocelot.Configuration.Parser;
|
||||||
using Ocelot.Configuration.Validator;
|
using Ocelot.Configuration.Validator;
|
||||||
using Ocelot.LoadBalancer.LoadBalancers;
|
using Ocelot.LoadBalancer.LoadBalancers;
|
||||||
|
using Ocelot.Requester.QoS;
|
||||||
using Ocelot.Responses;
|
using Ocelot.Responses;
|
||||||
using Shouldly;
|
using Shouldly;
|
||||||
using TestStack.BDDfy;
|
using TestStack.BDDfy;
|
||||||
@ -28,9 +29,15 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
private readonly Mock<ILoadBalancerFactory> _loadBalancerFactory;
|
private readonly Mock<ILoadBalancerFactory> _loadBalancerFactory;
|
||||||
private readonly Mock<ILoadBalancerHouse> _loadBalancerHouse;
|
private readonly Mock<ILoadBalancerHouse> _loadBalancerHouse;
|
||||||
private readonly Mock<ILoadBalancer> _loadBalancer;
|
private readonly Mock<ILoadBalancer> _loadBalancer;
|
||||||
|
private readonly Mock<IQoSProviderFactory> _qosProviderFactory;
|
||||||
|
private readonly Mock<IQosProviderHouse> _qosProviderHouse;
|
||||||
|
private readonly Mock<IQoSProvider> _qosProvider;
|
||||||
|
|
||||||
public FileConfigurationCreatorTests()
|
public FileConfigurationCreatorTests()
|
||||||
{
|
{
|
||||||
|
_qosProviderFactory = new Mock<IQoSProviderFactory>();
|
||||||
|
_qosProviderHouse = new Mock<IQosProviderHouse>();
|
||||||
|
_qosProvider = new Mock<IQoSProvider>();
|
||||||
_logger = new Mock<ILogger<FileOcelotConfigurationCreator>>();
|
_logger = new Mock<ILogger<FileOcelotConfigurationCreator>>();
|
||||||
_configParser = new Mock<IClaimToThingConfigurationParser>();
|
_configParser = new Mock<IClaimToThingConfigurationParser>();
|
||||||
_validator = new Mock<IConfigurationValidator>();
|
_validator = new Mock<IConfigurationValidator>();
|
||||||
@ -40,7 +47,8 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
_loadBalancer = new Mock<ILoadBalancer>();
|
_loadBalancer = new Mock<ILoadBalancer>();
|
||||||
_ocelotConfigurationCreator = new FileOcelotConfigurationCreator(
|
_ocelotConfigurationCreator = new FileOcelotConfigurationCreator(
|
||||||
_fileConfig.Object, _validator.Object, _configParser.Object, _logger.Object,
|
_fileConfig.Object, _validator.Object, _configParser.Object, _logger.Object,
|
||||||
_loadBalancerFactory.Object, _loadBalancerHouse.Object);
|
_loadBalancerFactory.Object, _loadBalancerHouse.Object,
|
||||||
|
_qosProviderFactory.Object, _qosProviderHouse.Object);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@ -53,7 +61,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
new FileReRoute
|
new FileReRoute
|
||||||
{
|
{
|
||||||
DownstreamHost = "127.0.0.1",
|
DownstreamHost = "127.0.0.1",
|
||||||
UpstreamTemplate = "/api/products/{productId}",
|
UpstreamPathTemplate = "/api/products/{productId}",
|
||||||
DownstreamPathTemplate = "/products/{productId}",
|
DownstreamPathTemplate = "/products/{productId}",
|
||||||
UpstreamHttpMethod = "Get",
|
UpstreamHttpMethod = "Get",
|
||||||
}
|
}
|
||||||
@ -64,7 +72,36 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
.When(x => x.WhenICreateTheConfig())
|
.When(x => x.WhenICreateTheConfig())
|
||||||
.Then(x => x.TheLoadBalancerFactoryIsCalledCorrectly())
|
.Then(x => x.TheLoadBalancerFactoryIsCalledCorrectly())
|
||||||
.And(x => x.ThenTheLoadBalancerHouseIsCalledCorrectly())
|
.And(x => x.ThenTheLoadBalancerHouseIsCalledCorrectly())
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_create_qos_provider()
|
||||||
|
{
|
||||||
|
this.Given(x => x.GivenTheConfigIs(new FileConfiguration
|
||||||
|
{
|
||||||
|
ReRoutes = new List<FileReRoute>
|
||||||
|
{
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamHost = "127.0.0.1",
|
||||||
|
UpstreamPathTemplate = "/api/products/{productId}",
|
||||||
|
DownstreamPathTemplate = "/products/{productId}",
|
||||||
|
UpstreamHttpMethod = "Get",
|
||||||
|
QoSOptions = new FileQoSOptions
|
||||||
|
{
|
||||||
|
TimeoutValue = 1,
|
||||||
|
DurationOfBreak = 1,
|
||||||
|
ExceptionsAllowedBeforeBreaking = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
.And(x => x.GivenTheConfigIsValid())
|
||||||
|
.And(x => x.GivenTheQosProviderFactoryReturns())
|
||||||
|
.When(x => x.WhenICreateTheConfig())
|
||||||
|
.Then(x => x.TheQosProviderFactoryIsCalledCorrectly())
|
||||||
|
.And(x => x.ThenTheQosProviderHouseIsCalledCorrectly())
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,7 +115,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
new FileReRoute
|
new FileReRoute
|
||||||
{
|
{
|
||||||
DownstreamHost = "127.0.0.1",
|
DownstreamHost = "127.0.0.1",
|
||||||
UpstreamTemplate = "/api/products/{productId}",
|
UpstreamPathTemplate = "/api/products/{productId}",
|
||||||
DownstreamPathTemplate = "/products/{productId}",
|
DownstreamPathTemplate = "/products/{productId}",
|
||||||
UpstreamHttpMethod = "Get",
|
UpstreamHttpMethod = "Get",
|
||||||
}
|
}
|
||||||
@ -91,7 +128,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
new ReRouteBuilder()
|
new ReRouteBuilder()
|
||||||
.WithDownstreamHost("127.0.0.1")
|
.WithDownstreamHost("127.0.0.1")
|
||||||
.WithDownstreamPathTemplate("/products/{productId}")
|
.WithDownstreamPathTemplate("/products/{productId}")
|
||||||
.WithUpstreamTemplate("/api/products/{productId}")
|
.WithUpstreamPathTemplate("/api/products/{productId}")
|
||||||
.WithUpstreamHttpMethod("Get")
|
.WithUpstreamHttpMethod("Get")
|
||||||
.WithUpstreamTemplatePattern("(?i)/api/products/.*/$")
|
.WithUpstreamTemplatePattern("(?i)/api/products/.*/$")
|
||||||
.Build()
|
.Build()
|
||||||
@ -109,7 +146,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
new FileReRoute
|
new FileReRoute
|
||||||
{
|
{
|
||||||
DownstreamScheme = "https",
|
DownstreamScheme = "https",
|
||||||
UpstreamTemplate = "/api/products/{productId}",
|
UpstreamPathTemplate = "/api/products/{productId}",
|
||||||
DownstreamPathTemplate = "/products/{productId}",
|
DownstreamPathTemplate = "/products/{productId}",
|
||||||
UpstreamHttpMethod = "Get",
|
UpstreamHttpMethod = "Get",
|
||||||
}
|
}
|
||||||
@ -122,7 +159,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
new ReRouteBuilder()
|
new ReRouteBuilder()
|
||||||
.WithDownstreamScheme("https")
|
.WithDownstreamScheme("https")
|
||||||
.WithDownstreamPathTemplate("/products/{productId}")
|
.WithDownstreamPathTemplate("/products/{productId}")
|
||||||
.WithUpstreamTemplate("/api/products/{productId}")
|
.WithUpstreamPathTemplate("/api/products/{productId}")
|
||||||
.WithUpstreamHttpMethod("Get")
|
.WithUpstreamHttpMethod("Get")
|
||||||
.WithUpstreamTemplatePattern("(?i)/api/products/.*/$")
|
.WithUpstreamTemplatePattern("(?i)/api/products/.*/$")
|
||||||
.Build()
|
.Build()
|
||||||
@ -139,7 +176,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
{
|
{
|
||||||
new FileReRoute
|
new FileReRoute
|
||||||
{
|
{
|
||||||
UpstreamTemplate = "/api/products/{productId}",
|
UpstreamPathTemplate = "/api/products/{productId}",
|
||||||
DownstreamPathTemplate = "/products/{productId}",
|
DownstreamPathTemplate = "/products/{productId}",
|
||||||
UpstreamHttpMethod = "Get",
|
UpstreamHttpMethod = "Get",
|
||||||
ReRouteIsCaseSensitive = false,
|
ReRouteIsCaseSensitive = false,
|
||||||
@ -161,13 +198,15 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
{
|
{
|
||||||
new ReRouteBuilder()
|
new ReRouteBuilder()
|
||||||
.WithDownstreamPathTemplate("/products/{productId}")
|
.WithDownstreamPathTemplate("/products/{productId}")
|
||||||
.WithUpstreamTemplate("/api/products/{productId}")
|
.WithUpstreamPathTemplate("/api/products/{productId}")
|
||||||
.WithUpstreamHttpMethod("Get")
|
.WithUpstreamHttpMethod("Get")
|
||||||
.WithUpstreamTemplatePattern("(?i)/api/products/.*/$")
|
.WithUpstreamTemplatePattern("(?i)/api/products/.*/$")
|
||||||
.WithServiceName("ProductService")
|
.WithServiceProviderConfiguraion(new ServiceProviderConfiguraionBuilder()
|
||||||
.WithUseServiceDiscovery(true)
|
.WithUseServiceDiscovery(true)
|
||||||
.WithServiceDiscoveryProvider("consul")
|
.WithServiceDiscoveryProvider("consul")
|
||||||
.WithServiceDiscoveryAddress("127.0.01")
|
.WithServiceDiscoveryProviderHost("127.0.0.1")
|
||||||
|
.WithServiceName("ProductService")
|
||||||
|
.Build())
|
||||||
.Build()
|
.Build()
|
||||||
}))
|
}))
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
@ -182,7 +221,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
{
|
{
|
||||||
new FileReRoute
|
new FileReRoute
|
||||||
{
|
{
|
||||||
UpstreamTemplate = "/api/products/{productId}",
|
UpstreamPathTemplate = "/api/products/{productId}",
|
||||||
DownstreamPathTemplate = "/products/{productId}",
|
DownstreamPathTemplate = "/products/{productId}",
|
||||||
UpstreamHttpMethod = "Get",
|
UpstreamHttpMethod = "Get",
|
||||||
ReRouteIsCaseSensitive = false,
|
ReRouteIsCaseSensitive = false,
|
||||||
@ -195,12 +234,12 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
{
|
{
|
||||||
new ReRouteBuilder()
|
new ReRouteBuilder()
|
||||||
.WithDownstreamPathTemplate("/products/{productId}")
|
.WithDownstreamPathTemplate("/products/{productId}")
|
||||||
.WithUpstreamTemplate("/api/products/{productId}")
|
.WithUpstreamPathTemplate("/api/products/{productId}")
|
||||||
.WithUpstreamHttpMethod("Get")
|
.WithUpstreamHttpMethod("Get")
|
||||||
.WithUpstreamTemplatePattern("(?i)/api/products/.*/$")
|
.WithUpstreamTemplatePattern("(?i)/api/products/.*/$")
|
||||||
|
.WithServiceProviderConfiguraion(new ServiceProviderConfiguraionBuilder()
|
||||||
.WithUseServiceDiscovery(false)
|
.WithUseServiceDiscovery(false)
|
||||||
.WithServiceDiscoveryProvider(null)
|
.Build())
|
||||||
.WithServiceDiscoveryAddress(null)
|
|
||||||
.Build()
|
.Build()
|
||||||
}))
|
}))
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
@ -215,7 +254,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
{
|
{
|
||||||
new FileReRoute
|
new FileReRoute
|
||||||
{
|
{
|
||||||
UpstreamTemplate = "/api/products/{productId}",
|
UpstreamPathTemplate = "/api/products/{productId}",
|
||||||
DownstreamPathTemplate = "/products/{productId}",
|
DownstreamPathTemplate = "/products/{productId}",
|
||||||
UpstreamHttpMethod = "Get",
|
UpstreamHttpMethod = "Get",
|
||||||
ReRouteIsCaseSensitive = false
|
ReRouteIsCaseSensitive = false
|
||||||
@ -228,7 +267,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
{
|
{
|
||||||
new ReRouteBuilder()
|
new ReRouteBuilder()
|
||||||
.WithDownstreamPathTemplate("/products/{productId}")
|
.WithDownstreamPathTemplate("/products/{productId}")
|
||||||
.WithUpstreamTemplate("/api/products/{productId}")
|
.WithUpstreamPathTemplate("/api/products/{productId}")
|
||||||
.WithUpstreamHttpMethod("Get")
|
.WithUpstreamHttpMethod("Get")
|
||||||
.WithUpstreamTemplatePattern("(?i)/api/products/.*/$")
|
.WithUpstreamTemplatePattern("(?i)/api/products/.*/$")
|
||||||
.Build()
|
.Build()
|
||||||
@ -245,7 +284,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
{
|
{
|
||||||
new FileReRoute
|
new FileReRoute
|
||||||
{
|
{
|
||||||
UpstreamTemplate = "/api/products/{productId}",
|
UpstreamPathTemplate = "/api/products/{productId}",
|
||||||
DownstreamPathTemplate = "/products/{productId}",
|
DownstreamPathTemplate = "/products/{productId}",
|
||||||
UpstreamHttpMethod = "Get"
|
UpstreamHttpMethod = "Get"
|
||||||
}
|
}
|
||||||
@ -257,7 +296,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
{
|
{
|
||||||
new ReRouteBuilder()
|
new ReRouteBuilder()
|
||||||
.WithDownstreamPathTemplate("/products/{productId}")
|
.WithDownstreamPathTemplate("/products/{productId}")
|
||||||
.WithUpstreamTemplate("/api/products/{productId}")
|
.WithUpstreamPathTemplate("/api/products/{productId}")
|
||||||
.WithUpstreamHttpMethod("Get")
|
.WithUpstreamHttpMethod("Get")
|
||||||
.WithUpstreamTemplatePattern("(?i)/api/products/.*/$")
|
.WithUpstreamTemplatePattern("(?i)/api/products/.*/$")
|
||||||
.Build()
|
.Build()
|
||||||
@ -274,7 +313,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
{
|
{
|
||||||
new FileReRoute
|
new FileReRoute
|
||||||
{
|
{
|
||||||
UpstreamTemplate = "/api/products/{productId}",
|
UpstreamPathTemplate = "/api/products/{productId}",
|
||||||
DownstreamPathTemplate = "/products/{productId}",
|
DownstreamPathTemplate = "/products/{productId}",
|
||||||
UpstreamHttpMethod = "Get",
|
UpstreamHttpMethod = "Get",
|
||||||
ReRouteIsCaseSensitive = true
|
ReRouteIsCaseSensitive = true
|
||||||
@ -287,7 +326,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
{
|
{
|
||||||
new ReRouteBuilder()
|
new ReRouteBuilder()
|
||||||
.WithDownstreamPathTemplate("/products/{productId}")
|
.WithDownstreamPathTemplate("/products/{productId}")
|
||||||
.WithUpstreamTemplate("/api/products/{productId}")
|
.WithUpstreamPathTemplate("/api/products/{productId}")
|
||||||
.WithUpstreamHttpMethod("Get")
|
.WithUpstreamHttpMethod("Get")
|
||||||
.WithUpstreamTemplatePattern("/api/products/.*/$")
|
.WithUpstreamTemplatePattern("/api/products/.*/$")
|
||||||
.Build()
|
.Build()
|
||||||
@ -304,7 +343,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
{
|
{
|
||||||
new FileReRoute
|
new FileReRoute
|
||||||
{
|
{
|
||||||
UpstreamTemplate = "/api/products/{productId}",
|
UpstreamPathTemplate = "/api/products/{productId}",
|
||||||
DownstreamPathTemplate = "/products/{productId}",
|
DownstreamPathTemplate = "/products/{productId}",
|
||||||
UpstreamHttpMethod = "Get",
|
UpstreamHttpMethod = "Get",
|
||||||
ReRouteIsCaseSensitive = true
|
ReRouteIsCaseSensitive = true
|
||||||
@ -321,7 +360,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
{
|
{
|
||||||
new ReRouteBuilder()
|
new ReRouteBuilder()
|
||||||
.WithDownstreamPathTemplate("/products/{productId}")
|
.WithDownstreamPathTemplate("/products/{productId}")
|
||||||
.WithUpstreamTemplate("/api/products/{productId}")
|
.WithUpstreamPathTemplate("/api/products/{productId}")
|
||||||
.WithUpstreamHttpMethod("Get")
|
.WithUpstreamHttpMethod("Get")
|
||||||
.WithUpstreamTemplatePattern("/api/products/.*/$")
|
.WithUpstreamTemplatePattern("/api/products/.*/$")
|
||||||
.WithRequestIdKey("blahhhh")
|
.WithRequestIdKey("blahhhh")
|
||||||
@ -339,7 +378,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
{
|
{
|
||||||
new FileReRoute
|
new FileReRoute
|
||||||
{
|
{
|
||||||
UpstreamTemplate = "/api/products/{productId}",
|
UpstreamPathTemplate = "/api/products/{productId}",
|
||||||
DownstreamPathTemplate = "/products/{productId}",
|
DownstreamPathTemplate = "/products/{productId}",
|
||||||
UpstreamHttpMethod = "Get",
|
UpstreamHttpMethod = "Get",
|
||||||
ReRouteIsCaseSensitive = true
|
ReRouteIsCaseSensitive = true
|
||||||
@ -352,7 +391,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
{
|
{
|
||||||
new ReRouteBuilder()
|
new ReRouteBuilder()
|
||||||
.WithDownstreamPathTemplate("/products/{productId}")
|
.WithDownstreamPathTemplate("/products/{productId}")
|
||||||
.WithUpstreamTemplate("/api/products/{productId}")
|
.WithUpstreamPathTemplate("/api/products/{productId}")
|
||||||
.WithUpstreamHttpMethod("Get")
|
.WithUpstreamHttpMethod("Get")
|
||||||
.WithUpstreamTemplatePattern("/api/products/.*/$")
|
.WithUpstreamTemplatePattern("/api/products/.*/$")
|
||||||
.Build()
|
.Build()
|
||||||
@ -363,18 +402,23 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void should_create_with_headers_to_extract()
|
public void should_create_with_headers_to_extract()
|
||||||
{
|
{
|
||||||
|
var authenticationOptions = new AuthenticationOptionsBuilder()
|
||||||
|
.WithProvider("IdentityServer")
|
||||||
|
.WithProviderRootUrl("http://localhost:51888")
|
||||||
|
.WithRequireHttps(false)
|
||||||
|
.WithScopeSecret("secret")
|
||||||
|
.WithScopeName("api")
|
||||||
|
.WithAdditionalScopes(new List<string>())
|
||||||
|
.Build();
|
||||||
|
|
||||||
var expected = new List<ReRoute>
|
var expected = new List<ReRoute>
|
||||||
{
|
{
|
||||||
new ReRouteBuilder()
|
new ReRouteBuilder()
|
||||||
.WithDownstreamPathTemplate("/products/{productId}")
|
.WithDownstreamPathTemplate("/products/{productId}")
|
||||||
.WithUpstreamTemplate("/api/products/{productId}")
|
.WithUpstreamPathTemplate("/api/products/{productId}")
|
||||||
.WithUpstreamHttpMethod("Get")
|
.WithUpstreamHttpMethod("Get")
|
||||||
.WithUpstreamTemplatePattern("/api/products/.*/$")
|
.WithUpstreamTemplatePattern("/api/products/.*/$")
|
||||||
.WithAuthenticationProvider("IdentityServer")
|
.WithAuthenticationOptions(authenticationOptions)
|
||||||
.WithAuthenticationProviderUrl("http://localhost:51888")
|
|
||||||
.WithRequireHttps(false)
|
|
||||||
.WithScopeSecret("secret")
|
|
||||||
.WithAuthenticationProviderScopeName("api")
|
|
||||||
.WithClaimsToHeaders(new List<ClaimToThing>
|
.WithClaimsToHeaders(new List<ClaimToThing>
|
||||||
{
|
{
|
||||||
new ClaimToThing("CustomerId", "CustomerId", "", 0),
|
new ClaimToThing("CustomerId", "CustomerId", "", 0),
|
||||||
@ -388,7 +432,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
{
|
{
|
||||||
new FileReRoute
|
new FileReRoute
|
||||||
{
|
{
|
||||||
UpstreamTemplate = "/api/products/{productId}",
|
UpstreamPathTemplate = "/api/products/{productId}",
|
||||||
DownstreamPathTemplate = "/products/{productId}",
|
DownstreamPathTemplate = "/products/{productId}",
|
||||||
UpstreamHttpMethod = "Get",
|
UpstreamHttpMethod = "Get",
|
||||||
ReRouteIsCaseSensitive = true,
|
ReRouteIsCaseSensitive = true,
|
||||||
@ -427,18 +471,23 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void should_create_with_authentication_properties()
|
public void should_create_with_authentication_properties()
|
||||||
{
|
{
|
||||||
|
var authenticationOptions = new AuthenticationOptionsBuilder()
|
||||||
|
.WithProvider("IdentityServer")
|
||||||
|
.WithProviderRootUrl("http://localhost:51888")
|
||||||
|
.WithRequireHttps(false)
|
||||||
|
.WithScopeSecret("secret")
|
||||||
|
.WithScopeName("api")
|
||||||
|
.WithAdditionalScopes(new List<string>())
|
||||||
|
.Build();
|
||||||
|
|
||||||
var expected = new List<ReRoute>
|
var expected = new List<ReRoute>
|
||||||
{
|
{
|
||||||
new ReRouteBuilder()
|
new ReRouteBuilder()
|
||||||
.WithDownstreamPathTemplate("/products/{productId}")
|
.WithDownstreamPathTemplate("/products/{productId}")
|
||||||
.WithUpstreamTemplate("/api/products/{productId}")
|
.WithUpstreamPathTemplate("/api/products/{productId}")
|
||||||
.WithUpstreamHttpMethod("Get")
|
.WithUpstreamHttpMethod("Get")
|
||||||
.WithUpstreamTemplatePattern("/api/products/.*/$")
|
.WithUpstreamTemplatePattern("/api/products/.*/$")
|
||||||
.WithAuthenticationProvider("IdentityServer")
|
.WithAuthenticationOptions(authenticationOptions)
|
||||||
.WithAuthenticationProviderUrl("http://localhost:51888")
|
|
||||||
.WithRequireHttps(false)
|
|
||||||
.WithScopeSecret("secret")
|
|
||||||
.WithAuthenticationProviderScopeName("api")
|
|
||||||
.Build()
|
.Build()
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -448,7 +497,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
{
|
{
|
||||||
new FileReRoute
|
new FileReRoute
|
||||||
{
|
{
|
||||||
UpstreamTemplate = "/api/products/{productId}",
|
UpstreamPathTemplate = "/api/products/{productId}",
|
||||||
DownstreamPathTemplate = "/products/{productId}",
|
DownstreamPathTemplate = "/products/{productId}",
|
||||||
UpstreamHttpMethod = "Get",
|
UpstreamHttpMethod = "Get",
|
||||||
ReRouteIsCaseSensitive = true,
|
ReRouteIsCaseSensitive = true,
|
||||||
@ -481,7 +530,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
{
|
{
|
||||||
new FileReRoute
|
new FileReRoute
|
||||||
{
|
{
|
||||||
UpstreamTemplate = "/api/products/{productId}/variants/{variantId}",
|
UpstreamPathTemplate = "/api/products/{productId}/variants/{variantId}",
|
||||||
DownstreamPathTemplate = "/products/{productId}",
|
DownstreamPathTemplate = "/products/{productId}",
|
||||||
UpstreamHttpMethod = "Get",
|
UpstreamHttpMethod = "Get",
|
||||||
ReRouteIsCaseSensitive = true
|
ReRouteIsCaseSensitive = true
|
||||||
@ -494,7 +543,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
{
|
{
|
||||||
new ReRouteBuilder()
|
new ReRouteBuilder()
|
||||||
.WithDownstreamPathTemplate("/products/{productId}")
|
.WithDownstreamPathTemplate("/products/{productId}")
|
||||||
.WithUpstreamTemplate("/api/products/{productId}/variants/{variantId}")
|
.WithUpstreamPathTemplate("/api/products/{productId}/variants/{variantId}")
|
||||||
.WithUpstreamHttpMethod("Get")
|
.WithUpstreamHttpMethod("Get")
|
||||||
.WithUpstreamTemplatePattern("/api/products/.*/variants/.*/$")
|
.WithUpstreamTemplatePattern("/api/products/.*/variants/.*/$")
|
||||||
.Build()
|
.Build()
|
||||||
@ -511,7 +560,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
{
|
{
|
||||||
new FileReRoute
|
new FileReRoute
|
||||||
{
|
{
|
||||||
UpstreamTemplate = "/api/products/{productId}/variants/{variantId}/",
|
UpstreamPathTemplate = "/api/products/{productId}/variants/{variantId}/",
|
||||||
DownstreamPathTemplate = "/products/{productId}",
|
DownstreamPathTemplate = "/products/{productId}",
|
||||||
UpstreamHttpMethod = "Get",
|
UpstreamHttpMethod = "Get",
|
||||||
ReRouteIsCaseSensitive = true
|
ReRouteIsCaseSensitive = true
|
||||||
@ -524,7 +573,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
{
|
{
|
||||||
new ReRouteBuilder()
|
new ReRouteBuilder()
|
||||||
.WithDownstreamPathTemplate("/products/{productId}")
|
.WithDownstreamPathTemplate("/products/{productId}")
|
||||||
.WithUpstreamTemplate("/api/products/{productId}/variants/{variantId}/")
|
.WithUpstreamPathTemplate("/api/products/{productId}/variants/{variantId}/")
|
||||||
.WithUpstreamHttpMethod("Get")
|
.WithUpstreamHttpMethod("Get")
|
||||||
.WithUpstreamTemplatePattern("/api/products/.*/variants/.*/$")
|
.WithUpstreamTemplatePattern("/api/products/.*/variants/.*/$")
|
||||||
.Build()
|
.Build()
|
||||||
@ -541,7 +590,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
{
|
{
|
||||||
new FileReRoute
|
new FileReRoute
|
||||||
{
|
{
|
||||||
UpstreamTemplate = "/",
|
UpstreamPathTemplate = "/",
|
||||||
DownstreamPathTemplate = "/api/products/",
|
DownstreamPathTemplate = "/api/products/",
|
||||||
UpstreamHttpMethod = "Get",
|
UpstreamHttpMethod = "Get",
|
||||||
ReRouteIsCaseSensitive = true
|
ReRouteIsCaseSensitive = true
|
||||||
@ -554,9 +603,9 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
{
|
{
|
||||||
new ReRouteBuilder()
|
new ReRouteBuilder()
|
||||||
.WithDownstreamPathTemplate("/api/products/")
|
.WithDownstreamPathTemplate("/api/products/")
|
||||||
.WithUpstreamTemplate("/")
|
.WithUpstreamPathTemplate("/")
|
||||||
.WithUpstreamHttpMethod("Get")
|
.WithUpstreamHttpMethod("Get")
|
||||||
.WithUpstreamTemplatePattern("/$")
|
.WithUpstreamTemplatePattern("^/$")
|
||||||
.Build()
|
.Build()
|
||||||
}))
|
}))
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
@ -591,7 +640,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
|
|
||||||
result.DownstreamPathTemplate.Value.ShouldBe(expected.DownstreamPathTemplate.Value);
|
result.DownstreamPathTemplate.Value.ShouldBe(expected.DownstreamPathTemplate.Value);
|
||||||
result.UpstreamHttpMethod.ShouldBe(expected.UpstreamHttpMethod);
|
result.UpstreamHttpMethod.ShouldBe(expected.UpstreamHttpMethod);
|
||||||
result.UpstreamTemplate.ShouldBe(expected.UpstreamTemplate);
|
result.UpstreamPathTemplate.Value.ShouldBe(expected.UpstreamPathTemplate.Value);
|
||||||
result.UpstreamTemplatePattern.ShouldBe(expected.UpstreamTemplatePattern);
|
result.UpstreamTemplatePattern.ShouldBe(expected.UpstreamTemplatePattern);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -631,5 +680,24 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
_loadBalancerHouse
|
_loadBalancerHouse
|
||||||
.Verify(x => x.Add(It.IsAny<string>(), _loadBalancer.Object), Times.Once);
|
.Verify(x => x.Add(It.IsAny<string>(), _loadBalancer.Object), Times.Once);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void GivenTheQosProviderFactoryReturns()
|
||||||
|
{
|
||||||
|
_qosProviderFactory
|
||||||
|
.Setup(x => x.Get(It.IsAny<ReRoute>()))
|
||||||
|
.Returns(_qosProvider.Object);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TheQosProviderFactoryIsCalledCorrectly()
|
||||||
|
{
|
||||||
|
_qosProviderFactory
|
||||||
|
.Verify(x => x.Get(It.IsAny<ReRoute>()), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThenTheQosProviderHouseIsCalledCorrectly()
|
||||||
|
{
|
||||||
|
_qosProviderHouse
|
||||||
|
.Verify(x => x.Add(It.IsAny<string>(), _qosProvider.Object), Times.Once);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -84,7 +84,10 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
|
|
||||||
public List<ReRoute> ReRoutes => new List<ReRoute>
|
public List<ReRoute> ReRoutes => new List<ReRoute>
|
||||||
{
|
{
|
||||||
new ReRouteBuilder().WithDownstreamPathTemplate(_downstreamTemplatePath).Build()
|
new ReRouteBuilder()
|
||||||
|
.WithDownstreamPathTemplate(_downstreamTemplatePath)
|
||||||
|
.WithUpstreamHttpMethod("Get")
|
||||||
|
.Build()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,13 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void should_call_scoped_data_repository_correctly()
|
public void should_call_scoped_data_repository_correctly()
|
||||||
{
|
{
|
||||||
this.Given(x => x.GivenTheDownStreamRouteFinderReturns(new DownstreamRoute(new List<UrlPathPlaceholderNameAndValue>(), new ReRouteBuilder().WithDownstreamPathTemplate("any old string").Build())))
|
this.Given(x => x.GivenTheDownStreamRouteFinderReturns(
|
||||||
|
new DownstreamRoute(
|
||||||
|
new List<UrlPathPlaceholderNameAndValue>(),
|
||||||
|
new ReRouteBuilder()
|
||||||
|
.WithDownstreamPathTemplate("any old string")
|
||||||
|
.WithUpstreamHttpMethod("Get")
|
||||||
|
.Build())))
|
||||||
.When(x => x.WhenICallTheMiddleware())
|
.When(x => x.WhenICallTheMiddleware())
|
||||||
.Then(x => x.ThenTheScopedDataRepositoryIsCalledCorrectly())
|
.Then(x => x.ThenTheScopedDataRepositoryIsCalledCorrectly())
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
|
@ -35,6 +35,37 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
|||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void should_return_route()
|
public void should_return_route()
|
||||||
|
{
|
||||||
|
this.Given(x => x.GivenThereIsAnUpstreamUrlPath("matchInUrlMatcher"))
|
||||||
|
.And(x =>x.GivenTheTemplateVariableAndNameFinderReturns(
|
||||||
|
new OkResponse<List<UrlPathPlaceholderNameAndValue>>(
|
||||||
|
new List<UrlPathPlaceholderNameAndValue>())))
|
||||||
|
.And(x => x.GivenTheConfigurationIs(new List<ReRoute>
|
||||||
|
{
|
||||||
|
new ReRouteBuilder()
|
||||||
|
.WithDownstreamPathTemplate("someDownstreamPath")
|
||||||
|
.WithUpstreamPathTemplate("someUpstreamPath")
|
||||||
|
.WithUpstreamHttpMethod("Get")
|
||||||
|
.WithUpstreamTemplatePattern("someUpstreamPath")
|
||||||
|
.Build()
|
||||||
|
}))
|
||||||
|
.And(x => x.GivenTheUrlMatcherReturns(new OkResponse<UrlMatch>(new UrlMatch(true))))
|
||||||
|
.And(x => x.GivenTheUpstreamHttpMethodIs("Get"))
|
||||||
|
.When(x => x.WhenICallTheFinder())
|
||||||
|
.Then(
|
||||||
|
x => x.ThenTheFollowingIsReturned(new DownstreamRoute(
|
||||||
|
new List<UrlPathPlaceholderNameAndValue>(),
|
||||||
|
new ReRouteBuilder()
|
||||||
|
.WithDownstreamPathTemplate("someDownstreamPath")
|
||||||
|
.WithUpstreamHttpMethod("Get")
|
||||||
|
.Build()
|
||||||
|
)))
|
||||||
|
.And(x => x.ThenTheUrlMatcherIsCalledCorrectly())
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_route_if_upstream_path_and_upstream_template_are_the_same()
|
||||||
{
|
{
|
||||||
this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath"))
|
this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath"))
|
||||||
.And(
|
.And(
|
||||||
@ -45,7 +76,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
|||||||
{
|
{
|
||||||
new ReRouteBuilder()
|
new ReRouteBuilder()
|
||||||
.WithDownstreamPathTemplate("someDownstreamPath")
|
.WithDownstreamPathTemplate("someDownstreamPath")
|
||||||
.WithUpstreamTemplate("someUpstreamPath")
|
.WithUpstreamPathTemplate("someUpstreamPath")
|
||||||
.WithUpstreamHttpMethod("Get")
|
.WithUpstreamHttpMethod("Get")
|
||||||
.WithUpstreamTemplatePattern("someUpstreamPath")
|
.WithUpstreamTemplatePattern("someUpstreamPath")
|
||||||
.Build()
|
.Build()
|
||||||
@ -58,9 +89,10 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
|||||||
x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List<UrlPathPlaceholderNameAndValue>(),
|
x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List<UrlPathPlaceholderNameAndValue>(),
|
||||||
new ReRouteBuilder()
|
new ReRouteBuilder()
|
||||||
.WithDownstreamPathTemplate("someDownstreamPath")
|
.WithDownstreamPathTemplate("someDownstreamPath")
|
||||||
|
.WithUpstreamHttpMethod("Get")
|
||||||
.Build()
|
.Build()
|
||||||
)))
|
)))
|
||||||
.And(x => x.ThenTheUrlMatcherIsCalledCorrectly())
|
.And(x => x.ThenTheUrlMatcherIsNotCalled())
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,13 +108,13 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
|||||||
{
|
{
|
||||||
new ReRouteBuilder()
|
new ReRouteBuilder()
|
||||||
.WithDownstreamPathTemplate("someDownstreamPath")
|
.WithDownstreamPathTemplate("someDownstreamPath")
|
||||||
.WithUpstreamTemplate("someUpstreamPath")
|
.WithUpstreamPathTemplate("someUpstreamPath")
|
||||||
.WithUpstreamHttpMethod("Get")
|
.WithUpstreamHttpMethod("Get")
|
||||||
.WithUpstreamTemplatePattern("")
|
.WithUpstreamTemplatePattern("")
|
||||||
.Build(),
|
.Build(),
|
||||||
new ReRouteBuilder()
|
new ReRouteBuilder()
|
||||||
.WithDownstreamPathTemplate("someDownstreamPathForAPost")
|
.WithDownstreamPathTemplate("someDownstreamPathForAPost")
|
||||||
.WithUpstreamTemplate("someUpstreamPath")
|
.WithUpstreamPathTemplate("someUpstreamPath")
|
||||||
.WithUpstreamHttpMethod("Post")
|
.WithUpstreamHttpMethod("Post")
|
||||||
.WithUpstreamTemplatePattern("")
|
.WithUpstreamTemplatePattern("")
|
||||||
.Build()
|
.Build()
|
||||||
@ -95,6 +127,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
|||||||
x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List<UrlPathPlaceholderNameAndValue>(),
|
x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List<UrlPathPlaceholderNameAndValue>(),
|
||||||
new ReRouteBuilder()
|
new ReRouteBuilder()
|
||||||
.WithDownstreamPathTemplate("someDownstreamPathForAPost")
|
.WithDownstreamPathTemplate("someDownstreamPathForAPost")
|
||||||
|
.WithUpstreamHttpMethod("Post")
|
||||||
.Build()
|
.Build()
|
||||||
)))
|
)))
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
@ -103,12 +136,12 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void should_not_return_route()
|
public void should_not_return_route()
|
||||||
{
|
{
|
||||||
this.Given(x => x.GivenThereIsAnUpstreamUrlPath("somePath"))
|
this.Given(x => x.GivenThereIsAnUpstreamUrlPath("dontMatchPath"))
|
||||||
.And(x => x.GivenTheConfigurationIs(new List<ReRoute>
|
.And(x => x.GivenTheConfigurationIs(new List<ReRoute>
|
||||||
{
|
{
|
||||||
new ReRouteBuilder()
|
new ReRouteBuilder()
|
||||||
.WithDownstreamPathTemplate("somPath")
|
.WithDownstreamPathTemplate("somPath")
|
||||||
.WithUpstreamTemplate("somePath")
|
.WithUpstreamPathTemplate("somePath")
|
||||||
.WithUpstreamHttpMethod("Get")
|
.WithUpstreamHttpMethod("Get")
|
||||||
.WithUpstreamTemplatePattern("somePath")
|
.WithUpstreamTemplatePattern("somePath")
|
||||||
.Build(),
|
.Build(),
|
||||||
@ -143,7 +176,13 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
|||||||
private void ThenTheUrlMatcherIsCalledCorrectly()
|
private void ThenTheUrlMatcherIsCalledCorrectly()
|
||||||
{
|
{
|
||||||
_mockMatcher
|
_mockMatcher
|
||||||
.Verify(x => x.Match(_upstreamUrlPath, _reRoutesConfig[0].UpstreamTemplate), Times.Once);
|
.Verify(x => x.Match(_upstreamUrlPath, _reRoutesConfig[0].UpstreamPathTemplate.Value), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThenTheUrlMatcherIsNotCalled()
|
||||||
|
{
|
||||||
|
_mockMatcher
|
||||||
|
.Verify(x => x.Match(_upstreamUrlPath, _reRoutesConfig[0].UpstreamPathTemplate.Value), Times.Never);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GivenTheUrlMatcherReturns(Response<UrlMatch> match)
|
private void GivenTheUrlMatcherReturns(Response<UrlMatch> match)
|
||||||
|
@ -18,6 +18,26 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder.UrlMatcher
|
|||||||
_urlMatcher = new RegExUrlMatcher();
|
_urlMatcher = new RegExUrlMatcher();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_not_match_forward_slash_only_regex()
|
||||||
|
{
|
||||||
|
this.Given(x => x.GivenIHaveAUpstreamPath("/working/"))
|
||||||
|
.And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("^/$"))
|
||||||
|
.When(x => x.WhenIMatchThePaths())
|
||||||
|
.And(x => x.ThenTheResultIsFalse())
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_match_forward_slash_only_regex()
|
||||||
|
{
|
||||||
|
this.Given(x => x.GivenIHaveAUpstreamPath("/"))
|
||||||
|
.And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("^/$"))
|
||||||
|
.When(x => x.WhenIMatchThePaths())
|
||||||
|
.And(x => x.ThenTheResultIsTrue())
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void should_find_match_when_template_smaller_than_valid_path()
|
public void should_find_match_when_template_smaller_than_valid_path()
|
||||||
{
|
{
|
||||||
|
@ -72,7 +72,13 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator
|
|||||||
{
|
{
|
||||||
var hostAndPort = new HostAndPort("127.0.0.1", 80);
|
var hostAndPort = new HostAndPort("127.0.0.1", 80);
|
||||||
|
|
||||||
this.Given(x => x.GivenTheDownStreamRouteIs(new DownstreamRoute(new List<UrlPathPlaceholderNameAndValue>(), new ReRouteBuilder().WithDownstreamPathTemplate("any old string").Build())))
|
this.Given(x => x.GivenTheDownStreamRouteIs(
|
||||||
|
new DownstreamRoute(
|
||||||
|
new List<UrlPathPlaceholderNameAndValue>(),
|
||||||
|
new ReRouteBuilder()
|
||||||
|
.WithDownstreamPathTemplate("any old string")
|
||||||
|
.WithUpstreamHttpMethod("Get")
|
||||||
|
.Build())))
|
||||||
.And(x => x.GivenTheHostAndPortIs(hostAndPort))
|
.And(x => x.GivenTheHostAndPortIs(hostAndPort))
|
||||||
.And(x => x.TheUrlReplacerReturns("/api/products/1"))
|
.And(x => x.TheUrlReplacerReturns("/api/products/1"))
|
||||||
.And(x => x.TheUrlBuilderReturns("http://127.0.0.1:80/api/products/1"))
|
.And(x => x.TheUrlBuilderReturns("http://127.0.0.1:80/api/products/1"))
|
||||||
@ -101,7 +107,7 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator
|
|||||||
{
|
{
|
||||||
_downstreamPath = new OkResponse<DownstreamPath>(new DownstreamPath(downstreamUrl));
|
_downstreamPath = new OkResponse<DownstreamPath>(new DownstreamPath(downstreamUrl));
|
||||||
_downstreamUrlTemplateVariableReplacer
|
_downstreamUrlTemplateVariableReplacer
|
||||||
.Setup(x => x.Replace(It.IsAny<DownstreamPathTemplate>(), It.IsAny<List<UrlPathPlaceholderNameAndValue>>()))
|
.Setup(x => x.Replace(It.IsAny<PathTemplate>(), It.IsAny<List<UrlPathPlaceholderNameAndValue>>()))
|
||||||
.Returns(_downstreamPath);
|
.Returns(_downstreamPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,7 +25,12 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator.UrlTemplateReplacer
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void can_replace_no_template_variables()
|
public void can_replace_no_template_variables()
|
||||||
{
|
{
|
||||||
this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(new List<UrlPathPlaceholderNameAndValue>(), new ReRouteBuilder().Build())))
|
this.Given(x => x.GivenThereIsAUrlMatch(
|
||||||
|
new DownstreamRoute(
|
||||||
|
new List<UrlPathPlaceholderNameAndValue>(),
|
||||||
|
new ReRouteBuilder()
|
||||||
|
.WithUpstreamHttpMethod("Get")
|
||||||
|
.Build())))
|
||||||
.When(x => x.WhenIReplaceTheTemplateVariables())
|
.When(x => x.WhenIReplaceTheTemplateVariables())
|
||||||
.Then(x => x.ThenTheDownstreamUrlPathIsReturned(""))
|
.Then(x => x.ThenTheDownstreamUrlPathIsReturned(""))
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
@ -34,7 +39,13 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator.UrlTemplateReplacer
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void can_replace_no_template_variables_with_slash()
|
public void can_replace_no_template_variables_with_slash()
|
||||||
{
|
{
|
||||||
this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(new List<UrlPathPlaceholderNameAndValue>(), new ReRouteBuilder().WithDownstreamPathTemplate("/").Build())))
|
this.Given(x => x.GivenThereIsAUrlMatch(
|
||||||
|
new DownstreamRoute(
|
||||||
|
new List<UrlPathPlaceholderNameAndValue>(),
|
||||||
|
new ReRouteBuilder()
|
||||||
|
.WithDownstreamPathTemplate("/")
|
||||||
|
.WithUpstreamHttpMethod("Get")
|
||||||
|
.Build())))
|
||||||
.When(x => x.WhenIReplaceTheTemplateVariables())
|
.When(x => x.WhenIReplaceTheTemplateVariables())
|
||||||
.Then(x => x.ThenTheDownstreamUrlPathIsReturned("/"))
|
.Then(x => x.ThenTheDownstreamUrlPathIsReturned("/"))
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
@ -43,7 +54,11 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator.UrlTemplateReplacer
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void can_replace_url_no_slash()
|
public void can_replace_url_no_slash()
|
||||||
{
|
{
|
||||||
this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(new List<UrlPathPlaceholderNameAndValue>(), new ReRouteBuilder().WithDownstreamPathTemplate("api").Build())))
|
this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(new List<UrlPathPlaceholderNameAndValue>(),
|
||||||
|
new ReRouteBuilder()
|
||||||
|
.WithDownstreamPathTemplate("api")
|
||||||
|
.WithUpstreamHttpMethod("Get")
|
||||||
|
.Build())))
|
||||||
.When(x => x.WhenIReplaceTheTemplateVariables())
|
.When(x => x.WhenIReplaceTheTemplateVariables())
|
||||||
.Then(x => x.ThenTheDownstreamUrlPathIsReturned("api"))
|
.Then(x => x.ThenTheDownstreamUrlPathIsReturned("api"))
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
@ -52,7 +67,11 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator.UrlTemplateReplacer
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void can_replace_url_one_slash()
|
public void can_replace_url_one_slash()
|
||||||
{
|
{
|
||||||
this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(new List<UrlPathPlaceholderNameAndValue>(), new ReRouteBuilder().WithDownstreamPathTemplate("api/").Build())))
|
this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(new List<UrlPathPlaceholderNameAndValue>(),
|
||||||
|
new ReRouteBuilder()
|
||||||
|
.WithDownstreamPathTemplate("api/")
|
||||||
|
.WithUpstreamHttpMethod("Get")
|
||||||
|
.Build())))
|
||||||
.When(x => x.WhenIReplaceTheTemplateVariables())
|
.When(x => x.WhenIReplaceTheTemplateVariables())
|
||||||
.Then(x => x.ThenTheDownstreamUrlPathIsReturned("api/"))
|
.Then(x => x.ThenTheDownstreamUrlPathIsReturned("api/"))
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
@ -61,7 +80,11 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator.UrlTemplateReplacer
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void can_replace_url_multiple_slash()
|
public void can_replace_url_multiple_slash()
|
||||||
{
|
{
|
||||||
this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(new List<UrlPathPlaceholderNameAndValue>(), new ReRouteBuilder().WithDownstreamPathTemplate("api/product/products/").Build())))
|
this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(new List<UrlPathPlaceholderNameAndValue>(),
|
||||||
|
new ReRouteBuilder()
|
||||||
|
.WithDownstreamPathTemplate("api/product/products/")
|
||||||
|
.WithUpstreamHttpMethod("Get")
|
||||||
|
.Build())))
|
||||||
.When(x => x.WhenIReplaceTheTemplateVariables())
|
.When(x => x.WhenIReplaceTheTemplateVariables())
|
||||||
.Then(x => x.ThenTheDownstreamUrlPathIsReturned("api/product/products/"))
|
.Then(x => x.ThenTheDownstreamUrlPathIsReturned("api/product/products/"))
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
@ -75,7 +98,11 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator.UrlTemplateReplacer
|
|||||||
new UrlPathPlaceholderNameAndValue("{productId}", "1")
|
new UrlPathPlaceholderNameAndValue("{productId}", "1")
|
||||||
};
|
};
|
||||||
|
|
||||||
this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(templateVariables, new ReRouteBuilder().WithDownstreamPathTemplate("productservice/products/{productId}/").Build())))
|
this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(templateVariables,
|
||||||
|
new ReRouteBuilder()
|
||||||
|
.WithDownstreamPathTemplate("productservice/products/{productId}/")
|
||||||
|
.WithUpstreamHttpMethod("Get")
|
||||||
|
.Build())))
|
||||||
.When(x => x.WhenIReplaceTheTemplateVariables())
|
.When(x => x.WhenIReplaceTheTemplateVariables())
|
||||||
.Then(x => x.ThenTheDownstreamUrlPathIsReturned("productservice/products/1/"))
|
.Then(x => x.ThenTheDownstreamUrlPathIsReturned("productservice/products/1/"))
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
@ -89,7 +116,11 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator.UrlTemplateReplacer
|
|||||||
new UrlPathPlaceholderNameAndValue("{productId}", "1")
|
new UrlPathPlaceholderNameAndValue("{productId}", "1")
|
||||||
};
|
};
|
||||||
|
|
||||||
this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(templateVariables, new ReRouteBuilder().WithDownstreamPathTemplate("productservice/products/{productId}/variants").Build())))
|
this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(templateVariables,
|
||||||
|
new ReRouteBuilder()
|
||||||
|
.WithDownstreamPathTemplate("productservice/products/{productId}/variants")
|
||||||
|
.WithUpstreamHttpMethod("Get")
|
||||||
|
.Build())))
|
||||||
.When(x => x.WhenIReplaceTheTemplateVariables())
|
.When(x => x.WhenIReplaceTheTemplateVariables())
|
||||||
.Then(x => x.ThenTheDownstreamUrlPathIsReturned("productservice/products/1/variants"))
|
.Then(x => x.ThenTheDownstreamUrlPathIsReturned("productservice/products/1/variants"))
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
@ -104,7 +135,11 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator.UrlTemplateReplacer
|
|||||||
new UrlPathPlaceholderNameAndValue("{variantId}", "12")
|
new UrlPathPlaceholderNameAndValue("{variantId}", "12")
|
||||||
};
|
};
|
||||||
|
|
||||||
this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(templateVariables, new ReRouteBuilder().WithDownstreamPathTemplate("productservice/products/{productId}/variants/{variantId}").Build())))
|
this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(templateVariables,
|
||||||
|
new ReRouteBuilder()
|
||||||
|
.WithDownstreamPathTemplate("productservice/products/{productId}/variants/{variantId}")
|
||||||
|
.WithUpstreamHttpMethod("Get")
|
||||||
|
.Build())))
|
||||||
.When(x => x.WhenIReplaceTheTemplateVariables())
|
.When(x => x.WhenIReplaceTheTemplateVariables())
|
||||||
.Then(x => x.ThenTheDownstreamUrlPathIsReturned("productservice/products/1/variants/12"))
|
.Then(x => x.ThenTheDownstreamUrlPathIsReturned("productservice/products/1/variants/12"))
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
@ -120,7 +155,11 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator.UrlTemplateReplacer
|
|||||||
new UrlPathPlaceholderNameAndValue("{categoryId}", "34")
|
new UrlPathPlaceholderNameAndValue("{categoryId}", "34")
|
||||||
};
|
};
|
||||||
|
|
||||||
this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(templateVariables, new ReRouteBuilder().WithDownstreamPathTemplate("productservice/category/{categoryId}/products/{productId}/variants/{variantId}").Build())))
|
this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(templateVariables,
|
||||||
|
new ReRouteBuilder()
|
||||||
|
.WithDownstreamPathTemplate("productservice/category/{categoryId}/products/{productId}/variants/{variantId}")
|
||||||
|
.WithUpstreamHttpMethod("Get")
|
||||||
|
.Build())))
|
||||||
.When(x => x.WhenIReplaceTheTemplateVariables())
|
.When(x => x.WhenIReplaceTheTemplateVariables())
|
||||||
.Then(x => x.ThenTheDownstreamUrlPathIsReturned("productservice/category/34/products/1/variants/12"))
|
.Then(x => x.ThenTheDownstreamUrlPathIsReturned("productservice/category/34/products/1/variants/12"))
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
|
@ -72,6 +72,7 @@ namespace Ocelot.UnitTests.Headers
|
|||||||
{
|
{
|
||||||
new ClaimToThing("UserId", "Subject", "", 0)
|
new ClaimToThing("UserId", "Subject", "", 0)
|
||||||
})
|
})
|
||||||
|
.WithUpstreamHttpMethod("Get")
|
||||||
.Build());
|
.Build());
|
||||||
|
|
||||||
this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute))
|
this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute))
|
||||||
|
@ -24,17 +24,12 @@ namespace Ocelot.UnitTests.LoadBalancer
|
|||||||
_factory = new LoadBalancerFactory(_serviceProviderFactory.Object);
|
_factory = new LoadBalancerFactory(_serviceProviderFactory.Object);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GivenTheServiceProviderFactoryReturns()
|
|
||||||
{
|
|
||||||
_serviceProviderFactory
|
|
||||||
.Setup(x => x.Get(It.IsAny<ServiceProviderConfiguraion>()))
|
|
||||||
.Returns(_serviceProvider.Object);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void should_return_no_load_balancer()
|
public void should_return_no_load_balancer()
|
||||||
{
|
{
|
||||||
var reRoute = new ReRouteBuilder()
|
var reRoute = new ReRouteBuilder()
|
||||||
|
.WithServiceProviderConfiguraion(new ServiceProviderConfiguraionBuilder().Build())
|
||||||
|
.WithUpstreamHttpMethod("Get")
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
this.Given(x => x.GivenAReRoute(reRoute))
|
this.Given(x => x.GivenAReRoute(reRoute))
|
||||||
@ -49,6 +44,8 @@ namespace Ocelot.UnitTests.LoadBalancer
|
|||||||
{
|
{
|
||||||
var reRoute = new ReRouteBuilder()
|
var reRoute = new ReRouteBuilder()
|
||||||
.WithLoadBalancer("RoundRobin")
|
.WithLoadBalancer("RoundRobin")
|
||||||
|
.WithUpstreamHttpMethod("Get")
|
||||||
|
.WithServiceProviderConfiguraion(new ServiceProviderConfiguraionBuilder().Build())
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
this.Given(x => x.GivenAReRoute(reRoute))
|
this.Given(x => x.GivenAReRoute(reRoute))
|
||||||
@ -63,6 +60,8 @@ namespace Ocelot.UnitTests.LoadBalancer
|
|||||||
{
|
{
|
||||||
var reRoute = new ReRouteBuilder()
|
var reRoute = new ReRouteBuilder()
|
||||||
.WithLoadBalancer("LeastConnection")
|
.WithLoadBalancer("LeastConnection")
|
||||||
|
.WithUpstreamHttpMethod("Get")
|
||||||
|
.WithServiceProviderConfiguraion(new ServiceProviderConfiguraionBuilder().Build())
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
this.Given(x => x.GivenAReRoute(reRoute))
|
this.Given(x => x.GivenAReRoute(reRoute))
|
||||||
@ -77,6 +76,8 @@ namespace Ocelot.UnitTests.LoadBalancer
|
|||||||
{
|
{
|
||||||
var reRoute = new ReRouteBuilder()
|
var reRoute = new ReRouteBuilder()
|
||||||
.WithLoadBalancer("RoundRobin")
|
.WithLoadBalancer("RoundRobin")
|
||||||
|
.WithUpstreamHttpMethod("Get")
|
||||||
|
.WithServiceProviderConfiguraion(new ServiceProviderConfiguraionBuilder().Build())
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
this.Given(x => x.GivenAReRoute(reRoute))
|
this.Given(x => x.GivenAReRoute(reRoute))
|
||||||
@ -86,6 +87,13 @@ namespace Ocelot.UnitTests.LoadBalancer
|
|||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void GivenTheServiceProviderFactoryReturns()
|
||||||
|
{
|
||||||
|
_serviceProviderFactory
|
||||||
|
.Setup(x => x.Get(It.IsAny<ServiceProviderConfiguraion>()))
|
||||||
|
.Returns(_serviceProvider.Object);
|
||||||
|
}
|
||||||
|
|
||||||
private void ThenTheServiceProviderIsCalledCorrectly()
|
private void ThenTheServiceProviderIsCalledCorrectly()
|
||||||
{
|
{
|
||||||
_serviceProviderFactory
|
_serviceProviderFactory
|
||||||
|
@ -68,6 +68,7 @@ namespace Ocelot.UnitTests.LoadBalancer
|
|||||||
{
|
{
|
||||||
var downstreamRoute = new DownstreamRoute(new List<Ocelot.DownstreamRouteFinder.UrlMatcher.UrlPathPlaceholderNameAndValue>(),
|
var downstreamRoute = new DownstreamRoute(new List<Ocelot.DownstreamRouteFinder.UrlMatcher.UrlPathPlaceholderNameAndValue>(),
|
||||||
new ReRouteBuilder()
|
new ReRouteBuilder()
|
||||||
|
.WithUpstreamHttpMethod("Get")
|
||||||
.Build());
|
.Build());
|
||||||
|
|
||||||
this.Given(x => x.GivenTheDownStreamUrlIs("any old string"))
|
this.Given(x => x.GivenTheDownStreamUrlIs("any old string"))
|
||||||
@ -84,6 +85,7 @@ namespace Ocelot.UnitTests.LoadBalancer
|
|||||||
{
|
{
|
||||||
var downstreamRoute = new DownstreamRoute(new List<Ocelot.DownstreamRouteFinder.UrlMatcher.UrlPathPlaceholderNameAndValue>(),
|
var downstreamRoute = new DownstreamRoute(new List<Ocelot.DownstreamRouteFinder.UrlMatcher.UrlPathPlaceholderNameAndValue>(),
|
||||||
new ReRouteBuilder()
|
new ReRouteBuilder()
|
||||||
|
.WithUpstreamHttpMethod("Get")
|
||||||
.Build());
|
.Build());
|
||||||
|
|
||||||
this.Given(x => x.GivenTheDownStreamUrlIs("any old string"))
|
this.Given(x => x.GivenTheDownStreamUrlIs("any old string"))
|
||||||
@ -99,6 +101,7 @@ namespace Ocelot.UnitTests.LoadBalancer
|
|||||||
{
|
{
|
||||||
var downstreamRoute = new DownstreamRoute(new List<Ocelot.DownstreamRouteFinder.UrlMatcher.UrlPathPlaceholderNameAndValue>(),
|
var downstreamRoute = new DownstreamRoute(new List<Ocelot.DownstreamRouteFinder.UrlMatcher.UrlPathPlaceholderNameAndValue>(),
|
||||||
new ReRouteBuilder()
|
new ReRouteBuilder()
|
||||||
|
.WithUpstreamHttpMethod("Get")
|
||||||
.Build());
|
.Build());
|
||||||
|
|
||||||
this.Given(x => x.GivenTheDownStreamUrlIs("any old string"))
|
this.Given(x => x.GivenTheDownStreamUrlIs("any old string"))
|
||||||
|
@ -70,6 +70,7 @@ namespace Ocelot.UnitTests.QueryStrings
|
|||||||
{
|
{
|
||||||
new ClaimToThing("UserId", "Subject", "", 0)
|
new ClaimToThing("UserId", "Subject", "", 0)
|
||||||
})
|
})
|
||||||
|
.WithUpstreamHttpMethod("Get")
|
||||||
.Build());
|
.Build());
|
||||||
|
|
||||||
this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute))
|
this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute))
|
||||||
|
@ -72,6 +72,7 @@ namespace Ocelot.UnitTests.RateLimit
|
|||||||
var downstreamRoute = new DownstreamRoute(new List<Ocelot.DownstreamRouteFinder.UrlMatcher.UrlPathPlaceholderNameAndValue>(),
|
var downstreamRoute = new DownstreamRoute(new List<Ocelot.DownstreamRouteFinder.UrlMatcher.UrlPathPlaceholderNameAndValue>(),
|
||||||
new ReRouteBuilder().WithEnableRateLimiting(true).WithRateLimitOptions(
|
new ReRouteBuilder().WithEnableRateLimiting(true).WithRateLimitOptions(
|
||||||
new Ocelot.Configuration.RateLimitOptions(true, "ClientId", new List<string>(), false, "", "", new Ocelot.Configuration.RateLimitRule("1s", TimeSpan.FromSeconds(100), 3), 429))
|
new Ocelot.Configuration.RateLimitOptions(true, "ClientId", new List<string>(), false, "", "", new Ocelot.Configuration.RateLimitRule("1s", TimeSpan.FromSeconds(100), 3), 429))
|
||||||
|
.WithUpstreamHttpMethod("Get")
|
||||||
.Build());
|
.Build());
|
||||||
|
|
||||||
this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute))
|
this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute))
|
||||||
@ -88,6 +89,7 @@ namespace Ocelot.UnitTests.RateLimit
|
|||||||
var downstreamRoute = new DownstreamRoute(new List<Ocelot.DownstreamRouteFinder.UrlMatcher.UrlPathPlaceholderNameAndValue>(),
|
var downstreamRoute = new DownstreamRoute(new List<Ocelot.DownstreamRouteFinder.UrlMatcher.UrlPathPlaceholderNameAndValue>(),
|
||||||
new ReRouteBuilder().WithEnableRateLimiting(true).WithRateLimitOptions(
|
new ReRouteBuilder().WithEnableRateLimiting(true).WithRateLimitOptions(
|
||||||
new Ocelot.Configuration.RateLimitOptions(true, "ClientId", new List<string>() { "ocelotclient2" }, false, "", "", new RateLimitRule( "1s", TimeSpan.FromSeconds(100),3),429))
|
new Ocelot.Configuration.RateLimitOptions(true, "ClientId", new List<string>() { "ocelotclient2" }, false, "", "", new RateLimitRule( "1s", TimeSpan.FromSeconds(100),3),429))
|
||||||
|
.WithUpstreamHttpMethod("Get")
|
||||||
.Build());
|
.Build());
|
||||||
|
|
||||||
this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute))
|
this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute))
|
||||||
|
@ -20,6 +20,7 @@ using Ocelot.Responses;
|
|||||||
using TestStack.BDDfy;
|
using TestStack.BDDfy;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using Ocelot.Configuration;
|
using Ocelot.Configuration;
|
||||||
|
using Ocelot.Requester.QoS;
|
||||||
|
|
||||||
namespace Ocelot.UnitTests.Request
|
namespace Ocelot.UnitTests.Request
|
||||||
{
|
{
|
||||||
@ -27,6 +28,7 @@ namespace Ocelot.UnitTests.Request
|
|||||||
{
|
{
|
||||||
private readonly Mock<IRequestCreator> _requestBuilder;
|
private readonly Mock<IRequestCreator> _requestBuilder;
|
||||||
private readonly Mock<IRequestScopedDataRepository> _scopedRepository;
|
private readonly Mock<IRequestScopedDataRepository> _scopedRepository;
|
||||||
|
private readonly Mock<IQosProviderHouse> _qosProviderHouse;
|
||||||
private readonly string _url;
|
private readonly string _url;
|
||||||
private readonly TestServer _server;
|
private readonly TestServer _server;
|
||||||
private readonly HttpClient _client;
|
private readonly HttpClient _client;
|
||||||
@ -38,6 +40,7 @@ namespace Ocelot.UnitTests.Request
|
|||||||
public HttpRequestBuilderMiddlewareTests()
|
public HttpRequestBuilderMiddlewareTests()
|
||||||
{
|
{
|
||||||
_url = "http://localhost:51879";
|
_url = "http://localhost:51879";
|
||||||
|
_qosProviderHouse = new Mock<IQosProviderHouse>();
|
||||||
_requestBuilder = new Mock<IRequestCreator>();
|
_requestBuilder = new Mock<IRequestCreator>();
|
||||||
_scopedRepository = new Mock<IRequestScopedDataRepository>();
|
_scopedRepository = new Mock<IRequestScopedDataRepository>();
|
||||||
var builder = new WebHostBuilder()
|
var builder = new WebHostBuilder()
|
||||||
@ -45,6 +48,7 @@ namespace Ocelot.UnitTests.Request
|
|||||||
{
|
{
|
||||||
x.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
|
x.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
|
||||||
x.AddLogging();
|
x.AddLogging();
|
||||||
|
x.AddSingleton(_qosProviderHouse.Object);
|
||||||
x.AddSingleton(_requestBuilder.Object);
|
x.AddSingleton(_requestBuilder.Object);
|
||||||
x.AddSingleton(_scopedRepository.Object);
|
x.AddSingleton(_scopedRepository.Object);
|
||||||
})
|
})
|
||||||
@ -68,17 +72,26 @@ namespace Ocelot.UnitTests.Request
|
|||||||
|
|
||||||
var downstreamRoute = new DownstreamRoute(new List<UrlPathPlaceholderNameAndValue>(),
|
var downstreamRoute = new DownstreamRoute(new List<UrlPathPlaceholderNameAndValue>(),
|
||||||
new ReRouteBuilder()
|
new ReRouteBuilder()
|
||||||
.WithRequestIdKey("LSRequestId").Build());
|
.WithRequestIdKey("LSRequestId")
|
||||||
|
.WithUpstreamHttpMethod("Get")
|
||||||
|
.Build());
|
||||||
|
|
||||||
this.Given(x => x.GivenTheDownStreamUrlIs("any old string"))
|
this.Given(x => x.GivenTheDownStreamUrlIs("any old string"))
|
||||||
|
.And(x => x.GivenTheQosProviderHouseReturns(new OkResponse<IQoSProvider>(new NoQoSProvider())))
|
||||||
.And(x => x.GivenTheDownStreamRouteIs(downstreamRoute))
|
.And(x => x.GivenTheDownStreamRouteIs(downstreamRoute))
|
||||||
.And(x => x.GivenTheRequestBuilderReturns(new Ocelot.Request.Request(new HttpRequestMessage(), new CookieContainer(), true, new QoSOptions(3, 8 ,5000, Polly.Timeout.TimeoutStrategy.Pessimistic))))
|
.And(x => x.GivenTheRequestBuilderReturns(new Ocelot.Request.Request(new HttpRequestMessage(), new CookieContainer(), true, new NoQoSProvider())))
|
||||||
.When(x => x.WhenICallTheMiddleware())
|
.When(x => x.WhenICallTheMiddleware())
|
||||||
.Then(x => x.ThenTheScopedDataRepositoryIsCalledCorrectly())
|
.Then(x => x.ThenTheScopedDataRepositoryIsCalledCorrectly())
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void GivenTheQosProviderHouseReturns(Response<IQoSProvider> qosProvider)
|
||||||
|
{
|
||||||
|
_qosProviderHouse
|
||||||
|
.Setup(x => x.Get(It.IsAny<string>()))
|
||||||
|
.Returns(qosProvider);
|
||||||
|
}
|
||||||
|
|
||||||
private void GivenTheDownStreamRouteIs(DownstreamRoute downstreamRoute)
|
private void GivenTheDownStreamRouteIs(DownstreamRoute downstreamRoute)
|
||||||
{
|
{
|
||||||
_downstreamRoute = new OkResponse<DownstreamRoute>(downstreamRoute);
|
_downstreamRoute = new OkResponse<DownstreamRoute>(downstreamRoute);
|
||||||
@ -92,7 +105,7 @@ namespace Ocelot.UnitTests.Request
|
|||||||
_request = new OkResponse<Ocelot.Request.Request>(request);
|
_request = new OkResponse<Ocelot.Request.Request>(request);
|
||||||
_requestBuilder
|
_requestBuilder
|
||||||
.Setup(x => x.Build(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<Stream>(), It.IsAny<IHeaderDictionary>(),
|
.Setup(x => x.Build(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<Stream>(), It.IsAny<IHeaderDictionary>(),
|
||||||
It.IsAny<IRequestCookieCollection>(), It.IsAny<QueryString>(), It.IsAny<string>(), It.IsAny<Ocelot.RequestId.RequestId>(),It.IsAny<bool>(), It.IsAny<QoSOptions>()))
|
It.IsAny<IRequestCookieCollection>(), It.IsAny<QueryString>(), It.IsAny<string>(), It.IsAny<Ocelot.RequestId.RequestId>(),It.IsAny<bool>(), It.IsAny<IQoSProvider>()))
|
||||||
.ReturnsAsync(_request);
|
.ReturnsAsync(_request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ using Shouldly;
|
|||||||
using TestStack.BDDfy;
|
using TestStack.BDDfy;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using Ocelot.Configuration;
|
using Ocelot.Configuration;
|
||||||
|
using Ocelot.Requester.QoS;
|
||||||
|
|
||||||
namespace Ocelot.UnitTests.Request
|
namespace Ocelot.UnitTests.Request
|
||||||
{
|
{
|
||||||
@ -27,7 +28,7 @@ namespace Ocelot.UnitTests.Request
|
|||||||
private Response<Ocelot.Request.Request> _result;
|
private Response<Ocelot.Request.Request> _result;
|
||||||
private Ocelot.RequestId.RequestId _requestId;
|
private Ocelot.RequestId.RequestId _requestId;
|
||||||
private bool _isQos;
|
private bool _isQos;
|
||||||
private QoSOptions _qos;
|
private IQoSProvider _qoSProvider;
|
||||||
|
|
||||||
public RequestBuilderTests()
|
public RequestBuilderTests()
|
||||||
{
|
{
|
||||||
@ -40,7 +41,7 @@ namespace Ocelot.UnitTests.Request
|
|||||||
{
|
{
|
||||||
this.Given(x => x.GivenIHaveHttpMethod("GET"))
|
this.Given(x => x.GivenIHaveHttpMethod("GET"))
|
||||||
.And(x => x.GivenIHaveDownstreamUrl("http://www.bbc.co.uk"))
|
.And(x => x.GivenIHaveDownstreamUrl("http://www.bbc.co.uk"))
|
||||||
.And(x=> x.GivenTheQos(true,new QoSOptions(3, 8, 5000, Polly.Timeout.TimeoutStrategy.Pessimistic)))
|
.And(x=> x.GivenTheQos(true, new NoQoSProvider()))
|
||||||
.When(x => x.WhenICreateARequest())
|
.When(x => x.WhenICreateARequest())
|
||||||
.And(x => x.ThenTheCorrectDownstreamUrlIsUsed("http://www.bbc.co.uk/"))
|
.And(x => x.ThenTheCorrectDownstreamUrlIsUsed("http://www.bbc.co.uk/"))
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
@ -51,7 +52,7 @@ namespace Ocelot.UnitTests.Request
|
|||||||
{
|
{
|
||||||
this.Given(x => x.GivenIHaveHttpMethod("POST"))
|
this.Given(x => x.GivenIHaveHttpMethod("POST"))
|
||||||
.And(x => x.GivenIHaveDownstreamUrl("http://www.bbc.co.uk"))
|
.And(x => x.GivenIHaveDownstreamUrl("http://www.bbc.co.uk"))
|
||||||
.And(x => x.GivenTheQos(true,new QoSOptions(3, 8, 5000, Polly.Timeout.TimeoutStrategy.Pessimistic)))
|
.And(x => x.GivenTheQos(true, new NoQoSProvider()))
|
||||||
|
|
||||||
.When(x => x.WhenICreateARequest())
|
.When(x => x.WhenICreateARequest())
|
||||||
.And(x => x.ThenTheCorrectHttpMethodIsUsed(HttpMethod.Post))
|
.And(x => x.ThenTheCorrectHttpMethodIsUsed(HttpMethod.Post))
|
||||||
@ -65,7 +66,7 @@ namespace Ocelot.UnitTests.Request
|
|||||||
.And(x => x.GivenIHaveDownstreamUrl("http://www.bbc.co.uk"))
|
.And(x => x.GivenIHaveDownstreamUrl("http://www.bbc.co.uk"))
|
||||||
.And(x => x.GivenIHaveTheHttpContent(new StringContent("Hi from Tom")))
|
.And(x => x.GivenIHaveTheHttpContent(new StringContent("Hi from Tom")))
|
||||||
.And(x => x.GivenTheContentTypeIs("application/json"))
|
.And(x => x.GivenTheContentTypeIs("application/json"))
|
||||||
.And(x => x.GivenTheQos(true, new QoSOptions(3, 8, 5000, Polly.Timeout.TimeoutStrategy.Pessimistic)))
|
.And(x => x.GivenTheQos(true, new NoQoSProvider()))
|
||||||
|
|
||||||
.When(x => x.WhenICreateARequest())
|
.When(x => x.WhenICreateARequest())
|
||||||
.And(x => x.ThenTheCorrectContentIsUsed(new StringContent("Hi from Tom")))
|
.And(x => x.ThenTheCorrectContentIsUsed(new StringContent("Hi from Tom")))
|
||||||
@ -79,7 +80,7 @@ namespace Ocelot.UnitTests.Request
|
|||||||
.And(x => x.GivenIHaveDownstreamUrl("http://www.bbc.co.uk"))
|
.And(x => x.GivenIHaveDownstreamUrl("http://www.bbc.co.uk"))
|
||||||
.And(x => x.GivenIHaveTheHttpContent(new StringContent("Hi from Tom")))
|
.And(x => x.GivenIHaveTheHttpContent(new StringContent("Hi from Tom")))
|
||||||
.And(x => x.GivenTheContentTypeIs("application/json"))
|
.And(x => x.GivenTheContentTypeIs("application/json"))
|
||||||
.And(x => x.GivenTheQos(true, new QoSOptions(3, 8, 5000, Polly.Timeout.TimeoutStrategy.Pessimistic)))
|
.And(x => x.GivenTheQos(true, new NoQoSProvider()))
|
||||||
|
|
||||||
.When(x => x.WhenICreateARequest())
|
.When(x => x.WhenICreateARequest())
|
||||||
.And(x => x.ThenTheCorrectContentHeadersAreUsed(new HeaderDictionary
|
.And(x => x.ThenTheCorrectContentHeadersAreUsed(new HeaderDictionary
|
||||||
@ -98,7 +99,7 @@ namespace Ocelot.UnitTests.Request
|
|||||||
.And(x => x.GivenIHaveDownstreamUrl("http://www.bbc.co.uk"))
|
.And(x => x.GivenIHaveDownstreamUrl("http://www.bbc.co.uk"))
|
||||||
.And(x => x.GivenIHaveTheHttpContent(new StringContent("Hi from Tom")))
|
.And(x => x.GivenIHaveTheHttpContent(new StringContent("Hi from Tom")))
|
||||||
.And(x => x.GivenTheContentTypeIs("application/json; charset=utf-8"))
|
.And(x => x.GivenTheContentTypeIs("application/json; charset=utf-8"))
|
||||||
.And(x => x.GivenTheQos(true, new QoSOptions(3, 8, 5000, Polly.Timeout.TimeoutStrategy.Pessimistic)))
|
.And(x => x.GivenTheQos(true, new NoQoSProvider()))
|
||||||
|
|
||||||
.When(x => x.WhenICreateARequest())
|
.When(x => x.WhenICreateARequest())
|
||||||
.And(x => x.ThenTheCorrectContentHeadersAreUsed(new HeaderDictionary
|
.And(x => x.ThenTheCorrectContentHeadersAreUsed(new HeaderDictionary
|
||||||
@ -119,7 +120,7 @@ namespace Ocelot.UnitTests.Request
|
|||||||
{
|
{
|
||||||
{"ChopSticks", "Bubbles" }
|
{"ChopSticks", "Bubbles" }
|
||||||
}))
|
}))
|
||||||
.And(x => x.GivenTheQos(true, new QoSOptions(3, 8, 5000, Polly.Timeout.TimeoutStrategy.Pessimistic)))
|
.And(x => x.GivenTheQos(true, new NoQoSProvider()))
|
||||||
|
|
||||||
.When(x => x.WhenICreateARequest())
|
.When(x => x.WhenICreateARequest())
|
||||||
.And(x => x.ThenTheCorrectHeadersAreUsed(new HeaderDictionary
|
.And(x => x.ThenTheCorrectHeadersAreUsed(new HeaderDictionary
|
||||||
@ -138,7 +139,7 @@ namespace Ocelot.UnitTests.Request
|
|||||||
.And(x => x.GivenIHaveDownstreamUrl("http://www.bbc.co.uk"))
|
.And(x => x.GivenIHaveDownstreamUrl("http://www.bbc.co.uk"))
|
||||||
.And(x => x.GivenTheHttpHeadersAre(new HeaderDictionary()))
|
.And(x => x.GivenTheHttpHeadersAre(new HeaderDictionary()))
|
||||||
.And(x => x.GivenTheRequestIdIs(new Ocelot.RequestId.RequestId("RequestId", requestId)))
|
.And(x => x.GivenTheRequestIdIs(new Ocelot.RequestId.RequestId("RequestId", requestId)))
|
||||||
.And(x => x.GivenTheQos(true, new QoSOptions(3, 8, 5000, Polly.Timeout.TimeoutStrategy.Pessimistic)))
|
.And(x => x.GivenTheQos(true, new NoQoSProvider()))
|
||||||
.When(x => x.WhenICreateARequest())
|
.When(x => x.WhenICreateARequest())
|
||||||
.And(x => x.ThenTheCorrectHeadersAreUsed(new HeaderDictionary
|
.And(x => x.ThenTheCorrectHeadersAreUsed(new HeaderDictionary
|
||||||
{
|
{
|
||||||
@ -157,7 +158,7 @@ namespace Ocelot.UnitTests.Request
|
|||||||
{"RequestId", "534534gv54gv45g" }
|
{"RequestId", "534534gv54gv45g" }
|
||||||
}))
|
}))
|
||||||
.And(x => x.GivenTheRequestIdIs(new Ocelot.RequestId.RequestId("RequestId", Guid.NewGuid().ToString())))
|
.And(x => x.GivenTheRequestIdIs(new Ocelot.RequestId.RequestId("RequestId", Guid.NewGuid().ToString())))
|
||||||
.And(x => x.GivenTheQos(true, new QoSOptions(3, 8, 5000, Polly.Timeout.TimeoutStrategy.Pessimistic)))
|
.And(x => x.GivenTheQos(true, new NoQoSProvider()))
|
||||||
.When(x => x.WhenICreateARequest())
|
.When(x => x.WhenICreateARequest())
|
||||||
.And(x => x.ThenTheCorrectHeadersAreUsed(new HeaderDictionary
|
.And(x => x.ThenTheCorrectHeadersAreUsed(new HeaderDictionary
|
||||||
{
|
{
|
||||||
@ -177,7 +178,7 @@ namespace Ocelot.UnitTests.Request
|
|||||||
.And(x => x.GivenIHaveDownstreamUrl("http://www.bbc.co.uk"))
|
.And(x => x.GivenIHaveDownstreamUrl("http://www.bbc.co.uk"))
|
||||||
.And(x => x.GivenTheHttpHeadersAre(new HeaderDictionary()))
|
.And(x => x.GivenTheHttpHeadersAre(new HeaderDictionary()))
|
||||||
.And(x => x.GivenTheRequestIdIs(new Ocelot.RequestId.RequestId(requestIdKey, requestIdValue)))
|
.And(x => x.GivenTheRequestIdIs(new Ocelot.RequestId.RequestId(requestIdKey, requestIdValue)))
|
||||||
.And(x => x.GivenTheQos(true, new QoSOptions(3, 8, 5000, Polly.Timeout.TimeoutStrategy.Pessimistic)))
|
.And(x => x.GivenTheQos(true, new NoQoSProvider()))
|
||||||
.When(x => x.WhenICreateARequest())
|
.When(x => x.WhenICreateARequest())
|
||||||
.And(x => x.ThenTheRequestIdIsNotInTheHeaders())
|
.And(x => x.ThenTheRequestIdIsNotInTheHeaders())
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
@ -188,10 +189,10 @@ namespace Ocelot.UnitTests.Request
|
|||||||
_requestId = requestId;
|
_requestId = requestId;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GivenTheQos(bool isQos, QoSOptions qos)
|
private void GivenTheQos(bool isQos, IQoSProvider qoSProvider)
|
||||||
{
|
{
|
||||||
_isQos = isQos;
|
_isQos = isQos;
|
||||||
_qos = qos;
|
_qoSProvider = qoSProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@ -304,7 +305,7 @@ namespace Ocelot.UnitTests.Request
|
|||||||
private void WhenICreateARequest()
|
private void WhenICreateARequest()
|
||||||
{
|
{
|
||||||
_result = _requestCreator.Build(_httpMethod, _downstreamUrl, _content?.ReadAsStreamAsync().Result, _headers,
|
_result = _requestCreator.Build(_httpMethod, _downstreamUrl, _content?.ReadAsStreamAsync().Result, _headers,
|
||||||
_cookies, _query, _contentType, _requestId,_isQos,_qos).Result;
|
_cookies, _query, _contentType, _requestId,_isQos,_qoSProvider).Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -72,7 +72,9 @@ namespace Ocelot.UnitTests.RequestId
|
|||||||
var downstreamRoute = new DownstreamRoute(new List<UrlPathPlaceholderNameAndValue>(),
|
var downstreamRoute = new DownstreamRoute(new List<UrlPathPlaceholderNameAndValue>(),
|
||||||
new ReRouteBuilder()
|
new ReRouteBuilder()
|
||||||
.WithDownstreamPathTemplate("any old string")
|
.WithDownstreamPathTemplate("any old string")
|
||||||
.WithRequestIdKey("LSRequestId").Build());
|
.WithRequestIdKey("LSRequestId")
|
||||||
|
.WithUpstreamHttpMethod("Get")
|
||||||
|
.Build());
|
||||||
|
|
||||||
var requestId = Guid.NewGuid().ToString();
|
var requestId = Guid.NewGuid().ToString();
|
||||||
|
|
||||||
@ -89,7 +91,9 @@ namespace Ocelot.UnitTests.RequestId
|
|||||||
var downstreamRoute = new DownstreamRoute(new List<UrlPathPlaceholderNameAndValue>(),
|
var downstreamRoute = new DownstreamRoute(new List<UrlPathPlaceholderNameAndValue>(),
|
||||||
new ReRouteBuilder()
|
new ReRouteBuilder()
|
||||||
.WithDownstreamPathTemplate("any old string")
|
.WithDownstreamPathTemplate("any old string")
|
||||||
.WithRequestIdKey("LSRequestId").Build());
|
.WithRequestIdKey("LSRequestId")
|
||||||
|
.WithUpstreamHttpMethod("Get")
|
||||||
|
.Build());
|
||||||
|
|
||||||
this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute))
|
this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute))
|
||||||
.When(x => x.WhenICallTheMiddleware())
|
.When(x => x.WhenICallTheMiddleware())
|
||||||
|
@ -13,6 +13,7 @@ using Ocelot.Logging;
|
|||||||
using Ocelot.QueryStrings.Middleware;
|
using Ocelot.QueryStrings.Middleware;
|
||||||
using Ocelot.Requester;
|
using Ocelot.Requester;
|
||||||
using Ocelot.Requester.Middleware;
|
using Ocelot.Requester.Middleware;
|
||||||
|
using Ocelot.Requester.QoS;
|
||||||
using Ocelot.Responder;
|
using Ocelot.Responder;
|
||||||
using Ocelot.Responses;
|
using Ocelot.Responses;
|
||||||
using TestStack.BDDfy;
|
using TestStack.BDDfy;
|
||||||
@ -61,7 +62,7 @@ namespace Ocelot.UnitTests.Requester
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void should_call_scoped_data_repository_correctly()
|
public void should_call_scoped_data_repository_correctly()
|
||||||
{
|
{
|
||||||
this.Given(x => x.GivenTheRequestIs(new Ocelot.Request.Request(new HttpRequestMessage(),new CookieContainer(),true, new Ocelot.Configuration.QoSOptions(3, 8, 5000, Polly.Timeout.TimeoutStrategy.Pessimistic))))
|
this.Given(x => x.GivenTheRequestIs(new Ocelot.Request.Request(new HttpRequestMessage(),new CookieContainer(),true, new NoQoSProvider())))
|
||||||
.And(x => x.GivenTheRequesterReturns(new HttpResponseMessage()))
|
.And(x => x.GivenTheRequesterReturns(new HttpResponseMessage()))
|
||||||
.And(x => x.GivenTheScopedRepoReturns())
|
.And(x => x.GivenTheScopedRepoReturns())
|
||||||
.When(x => x.WhenICallTheMiddleware())
|
.When(x => x.WhenICallTheMiddleware())
|
||||||
|
80
test/Ocelot.UnitTests/Requester/QoSProviderFactoryTests.cs
Normal file
80
test/Ocelot.UnitTests/Requester/QoSProviderFactoryTests.cs
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
using Moq;
|
||||||
|
using Ocelot.Configuration;
|
||||||
|
using Ocelot.Configuration.Builder;
|
||||||
|
using Ocelot.Logging;
|
||||||
|
using Ocelot.Requester.QoS;
|
||||||
|
using Shouldly;
|
||||||
|
using TestStack.BDDfy;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Ocelot.UnitTests.Requester
|
||||||
|
{
|
||||||
|
public class QoSProviderFactoryTests
|
||||||
|
{
|
||||||
|
private readonly IQoSProviderFactory _factory;
|
||||||
|
private ReRoute _reRoute;
|
||||||
|
private IQoSProvider _result;
|
||||||
|
private Mock<IOcelotLoggerFactory> _loggerFactory;
|
||||||
|
private Mock<IOcelotLogger> _logger;
|
||||||
|
|
||||||
|
public QoSProviderFactoryTests()
|
||||||
|
{
|
||||||
|
_logger = new Mock<IOcelotLogger>();
|
||||||
|
_loggerFactory = new Mock<IOcelotLoggerFactory>();
|
||||||
|
_loggerFactory
|
||||||
|
.Setup(x => x.CreateLogger<PollyQoSProvider>())
|
||||||
|
.Returns(_logger.Object);
|
||||||
|
_factory = new QoSProviderFactory(_loggerFactory.Object);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_no_qos_provider()
|
||||||
|
{
|
||||||
|
var reRoute = new ReRouteBuilder()
|
||||||
|
.WithUpstreamHttpMethod("get")
|
||||||
|
.WithIsQos(false)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
this.Given(x => x.GivenAReRoute(reRoute))
|
||||||
|
.When(x => x.WhenIGetTheQoSProvider())
|
||||||
|
.Then(x => x.ThenTheQoSProviderIsReturned<NoQoSProvider>())
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_polly_qos_provider()
|
||||||
|
{
|
||||||
|
var qosOptions = new QoSOptionsBuilder()
|
||||||
|
.WithTimeoutValue(100)
|
||||||
|
.WithDurationOfBreak(100)
|
||||||
|
.WithExceptionsAllowedBeforeBreaking(100)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var reRoute = new ReRouteBuilder()
|
||||||
|
.WithUpstreamHttpMethod("get")
|
||||||
|
.WithIsQos(true)
|
||||||
|
.WithQosOptions(qosOptions)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
this.Given(x => x.GivenAReRoute(reRoute))
|
||||||
|
.When(x => x.WhenIGetTheQoSProvider())
|
||||||
|
.Then(x => x.ThenTheQoSProviderIsReturned<PollyQoSProvider>())
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenAReRoute(ReRoute reRoute)
|
||||||
|
{
|
||||||
|
_reRoute = reRoute;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WhenIGetTheQoSProvider()
|
||||||
|
{
|
||||||
|
_result = _factory.Get(_reRoute);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThenTheQoSProviderIsReturned<T>()
|
||||||
|
{
|
||||||
|
_result.ShouldBeOfType<T>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
117
test/Ocelot.UnitTests/Requester/QosProviderHouseTests.cs
Normal file
117
test/Ocelot.UnitTests/Requester/QosProviderHouseTests.cs
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
using Ocelot.Requester.QoS;
|
||||||
|
using Ocelot.Responses;
|
||||||
|
using Shouldly;
|
||||||
|
using TestStack.BDDfy;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Ocelot.UnitTests.Requester
|
||||||
|
{
|
||||||
|
public class QosProviderHouseTests
|
||||||
|
{
|
||||||
|
private IQoSProvider _qoSProvider;
|
||||||
|
private readonly QosProviderHouse _qosProviderHouse;
|
||||||
|
private Response _addResult;
|
||||||
|
private Response<IQoSProvider> _getResult;
|
||||||
|
private string _key;
|
||||||
|
|
||||||
|
public QosProviderHouseTests()
|
||||||
|
{
|
||||||
|
_qosProviderHouse = new QosProviderHouse();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_store_qos_provider()
|
||||||
|
{
|
||||||
|
var key = "test";
|
||||||
|
|
||||||
|
this.Given(x => x.GivenThereIsAQoSProvider(key, new FakeQoSProvider()))
|
||||||
|
.When(x => x.WhenIAddTheQoSProvider())
|
||||||
|
.Then(x => x.ThenItIsAdded())
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_get_qos_provider()
|
||||||
|
{
|
||||||
|
var key = "test";
|
||||||
|
|
||||||
|
this.Given(x => x.GivenThereIsAQoSProvider(key, new FakeQoSProvider()))
|
||||||
|
.When(x => x.WhenWeGetTheQoSProvider(key))
|
||||||
|
.Then(x => x.ThenItIsReturned())
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_store_qos_providers_by_key()
|
||||||
|
{
|
||||||
|
var key = "test";
|
||||||
|
var keyTwo = "testTwo";
|
||||||
|
|
||||||
|
this.Given(x => x.GivenThereIsAQoSProvider(key, new FakeQoSProvider()))
|
||||||
|
.And(x => x.GivenThereIsAQoSProvider(keyTwo, new FakePollyQoSProvider()))
|
||||||
|
.When(x => x.WhenWeGetTheQoSProvider(key))
|
||||||
|
.Then(x => x.ThenTheQoSProviderIs<FakeQoSProvider>())
|
||||||
|
.When(x => x.WhenWeGetTheQoSProvider(keyTwo))
|
||||||
|
.Then(x => x.ThenTheQoSProviderIs<FakePollyQoSProvider>())
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_error_if_no_qos_provider_with_key()
|
||||||
|
{
|
||||||
|
this.When(x => x.WhenWeGetTheQoSProvider("test"))
|
||||||
|
.Then(x => x.ThenAnErrorIsReturned())
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThenAnErrorIsReturned()
|
||||||
|
{
|
||||||
|
_getResult.IsError.ShouldBeTrue();
|
||||||
|
_getResult.Errors[0].ShouldBeOfType<UnableToFindQoSProviderError>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThenTheQoSProviderIs<T>()
|
||||||
|
{
|
||||||
|
_getResult.Data.ShouldBeOfType<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThenItIsAdded()
|
||||||
|
{
|
||||||
|
_addResult.IsError.ShouldBe(false);
|
||||||
|
_addResult.ShouldBeOfType<OkResponse>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WhenIAddTheQoSProvider()
|
||||||
|
{
|
||||||
|
_addResult = _qosProviderHouse.Add(_key, _qoSProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void GivenThereIsAQoSProvider(string key, IQoSProvider qoSProvider)
|
||||||
|
{
|
||||||
|
_key = key;
|
||||||
|
_qoSProvider = qoSProvider;
|
||||||
|
WhenIAddTheQoSProvider();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WhenWeGetTheQoSProvider(string key)
|
||||||
|
{
|
||||||
|
_getResult = _qosProviderHouse.Get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThenItIsReturned()
|
||||||
|
{
|
||||||
|
_getResult.Data.ShouldBe(_qoSProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
class FakeQoSProvider : IQoSProvider
|
||||||
|
{
|
||||||
|
public CircuitBreaker CircuitBreaker { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
class FakePollyQoSProvider : IQoSProvider
|
||||||
|
{
|
||||||
|
public CircuitBreaker CircuitBreaker { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,8 @@
|
|||||||
using System.Collections.Generic;
|
using System;
|
||||||
using System.IO;
|
using System.Collections.Generic;
|
||||||
using System.Net.Http;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Ocelot.Errors;
|
using Ocelot.Errors;
|
||||||
using Ocelot.Middleware;
|
using Ocelot.Middleware;
|
||||||
|
using Ocelot.Requester;
|
||||||
using Ocelot.Responder;
|
using Ocelot.Responder;
|
||||||
using Ocelot.Responses;
|
using Ocelot.Responses;
|
||||||
using Shouldly;
|
using Shouldly;
|
||||||
@ -23,6 +22,18 @@ namespace Ocelot.UnitTests.Responder
|
|||||||
_codeMapper = new ErrorsToHttpStatusCodeMapper();
|
_codeMapper = new ErrorsToHttpStatusCodeMapper();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_timeout()
|
||||||
|
{
|
||||||
|
this.Given(x => x.GivenThereAreErrors(new List<Error>
|
||||||
|
{
|
||||||
|
new RequestTimedOutError(new Exception())
|
||||||
|
}))
|
||||||
|
.When(x => x.WhenIGetErrorStatusCode())
|
||||||
|
.Then(x => x.ThenTheResponseIsStatusCodeIs(503))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void should_create_unauthenticated_response_code()
|
public void should_create_unauthenticated_response_code()
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using Ocelot.Configuration;
|
using Ocelot.Configuration;
|
||||||
|
using Ocelot.Configuration.Builder;
|
||||||
using Ocelot.ServiceDiscovery;
|
using Ocelot.ServiceDiscovery;
|
||||||
using Shouldly;
|
using Shouldly;
|
||||||
using TestStack.BDDfy;
|
using TestStack.BDDfy;
|
||||||
@ -20,7 +21,11 @@ namespace Ocelot.UnitTests.ServiceDiscovery
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void should_return_no_service_provider()
|
public void should_return_no_service_provider()
|
||||||
{
|
{
|
||||||
var serviceConfig = new ServiceProviderConfiguraion("product", "127.0.0.1", 80, false, "Does not matter", string.Empty, 0);
|
var serviceConfig = new ServiceProviderConfiguraionBuilder()
|
||||||
|
.WithDownstreamHost("127.0.0.1")
|
||||||
|
.WithDownstreamPort(80)
|
||||||
|
.WithUseServiceDiscovery(false)
|
||||||
|
.Build();
|
||||||
|
|
||||||
this.Given(x => x.GivenTheReRoute(serviceConfig))
|
this.Given(x => x.GivenTheReRoute(serviceConfig))
|
||||||
.When(x => x.WhenIGetTheServiceProvider())
|
.When(x => x.WhenIGetTheServiceProvider())
|
||||||
@ -31,7 +36,11 @@ namespace Ocelot.UnitTests.ServiceDiscovery
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void should_return_consul_service_provider()
|
public void should_return_consul_service_provider()
|
||||||
{
|
{
|
||||||
var serviceConfig = new ServiceProviderConfiguraion("product", string.Empty, 0, true, "Consul", string.Empty, 0);
|
var serviceConfig = new ServiceProviderConfiguraionBuilder()
|
||||||
|
.WithServiceName("product")
|
||||||
|
.WithUseServiceDiscovery(true)
|
||||||
|
.WithServiceDiscoveryProvider("Consul")
|
||||||
|
.Build();
|
||||||
|
|
||||||
this.Given(x => x.GivenTheReRoute(serviceConfig))
|
this.Given(x => x.GivenTheReRoute(serviceConfig))
|
||||||
.When(x => x.WhenIGetTheServiceProvider())
|
.When(x => x.WhenIGetTheServiceProvider())
|
||||||
|
Loading…
x
Reference in New Issue
Block a user