mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-06-19 10:58:15 +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:
@ -12,7 +12,7 @@ namespace Ocelot.Authentication.Handler.Creator
|
||||
/// </summary>
|
||||
public class AuthenticationHandlerCreator : IAuthenticationHandlerCreator
|
||||
{
|
||||
public Response<RequestDelegate> CreateIdentityServerAuthenticationHandler(IApplicationBuilder app, AuthenticationOptions authOptions)
|
||||
public Response<RequestDelegate> Create(IApplicationBuilder app, AuthenticationOptions authOptions)
|
||||
{
|
||||
var builder = app.New();
|
||||
|
||||
|
@ -8,6 +8,6 @@ namespace Ocelot.Authentication.Handler.Creator
|
||||
|
||||
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)
|
||||
{
|
||||
var handler = _creator.CreateIdentityServerAuthenticationHandler(app, authOptions);
|
||||
var handler = _creator.Create(app, authOptions);
|
||||
|
||||
if (!handler.IsError)
|
||||
{
|
||||
|
@ -2,7 +2,6 @@
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Ocelot.Authentication.Handler.Factory;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Errors;
|
||||
|
@ -1,5 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using Ocelot.Errors;
|
||||
using Ocelot.Responses;
|
||||
|
@ -1,5 +1,4 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Ocelot.Infrastructure.RequestData;
|
||||
using Ocelot.Infrastructure.RequestData;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Responses;
|
||||
|
||||
@ -61,7 +60,7 @@ namespace Ocelot.Authorisation.Middleware
|
||||
SetPipelineError(new List<Error>
|
||||
{
|
||||
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 UpstreamTemplate { get; set; }
|
||||
public string UpstreamPathTemplate { get; set; }
|
||||
public string UpstreamHttpMethod { get; set; }
|
||||
public FileAuthenticationOptions AuthenticationOptions { 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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@ -94,18 +94,18 @@ namespace Ocelot.Configuration.Validator
|
||||
private ConfigurationValidationResult CheckForDupliateReRoutes(FileConfiguration configuration)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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());
|
||||
|
||||
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>()
|
||||
.ToList();
|
||||
|
||||
|
@ -28,6 +28,7 @@ using Ocelot.Logging;
|
||||
using Ocelot.QueryStrings;
|
||||
using Ocelot.Request.Builder;
|
||||
using Ocelot.Requester;
|
||||
using Ocelot.Requester.QoS;
|
||||
using Ocelot.Responder;
|
||||
using Ocelot.ServiceDiscovery;
|
||||
using Ocelot.RateLimit;
|
||||
@ -62,6 +63,8 @@ namespace Ocelot.DependencyInjection
|
||||
{
|
||||
services.AddMvcCore().AddJsonFormatters();
|
||||
services.AddLogging();
|
||||
services.AddSingleton<IQosProviderHouse, QosProviderHouse>();
|
||||
services.AddSingleton<IQoSProviderFactory, QoSProviderFactory>();
|
||||
services.AddSingleton<IServiceDiscoveryProviderFactory, ServiceDiscoveryProviderFactory>();
|
||||
services.AddSingleton<ILoadBalancerFactory, LoadBalancerFactory>();
|
||||
services.AddSingleton<ILoadBalancerHouse, LoadBalancerHouse>();
|
||||
|
@ -26,15 +26,22 @@ namespace Ocelot.DownstreamRouteFinder.Finder
|
||||
{
|
||||
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)
|
||||
{
|
||||
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);
|
||||
|
||||
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));
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ namespace Ocelot.DownstreamUrlCreator.UrlTemplateReplacer
|
||||
{
|
||||
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();
|
||||
|
||||
|
@ -7,6 +7,6 @@ namespace Ocelot.DownstreamUrlCreator.UrlTemplateReplacer
|
||||
{
|
||||
public interface IDownstreamPathPlaceholderReplacer
|
||||
{
|
||||
Response<DownstreamPath> Replace(DownstreamPathTemplate downstreamPathTemplate, List<UrlPathPlaceholderNameAndValue> urlPathPlaceholderNameAndValues);
|
||||
Response<DownstreamPath> Replace(PathTemplate downstreamPathTemplate, List<UrlPathPlaceholderNameAndValue> urlPathPlaceholderNameAndValues);
|
||||
}
|
||||
}
|
@ -25,6 +25,8 @@
|
||||
ServicesAreNullError,
|
||||
ServicesAreEmptyError,
|
||||
UnableToFindServiceDiscoveryProviderError,
|
||||
UnableToFindLoadBalancerError
|
||||
UnableToFindLoadBalancerError,
|
||||
RequestTimedOutError,
|
||||
UnableToFindQoSProviderError
|
||||
}
|
||||
}
|
||||
|
@ -13,17 +13,8 @@ namespace Ocelot.LoadBalancer.LoadBalancers
|
||||
}
|
||||
|
||||
public async Task<ILoadBalancer> Get(ReRoute reRoute)
|
||||
{
|
||||
var serviceConfig = new 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);
|
||||
{
|
||||
var serviceProvider = _serviceProviderFactory.Get(reRoute.ServiceProviderConfiguraion);
|
||||
|
||||
switch (reRoute.LoadBalancer)
|
||||
{
|
||||
|
@ -6,7 +6,6 @@ using Ocelot.LoadBalancer.LoadBalancers;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware;
|
||||
using Ocelot.QueryStrings.Middleware;
|
||||
using Ocelot.ServiceDiscovery;
|
||||
|
||||
namespace Ocelot.LoadBalancer.Middleware
|
||||
{
|
||||
@ -31,7 +30,7 @@ namespace Ocelot.LoadBalancer.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)
|
||||
{
|
||||
SetPipelineError(loadBalancer.Errors);
|
||||
|
@ -105,7 +105,7 @@ namespace Ocelot.RateLimit.Middleware
|
||||
|
||||
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)
|
||||
|
@ -3,6 +3,7 @@ using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.Responses;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Requester.QoS;
|
||||
|
||||
namespace Ocelot.Request.Builder
|
||||
{
|
||||
@ -18,7 +19,7 @@ namespace Ocelot.Request.Builder
|
||||
string contentType,
|
||||
RequestId.RequestId requestId,
|
||||
bool isQos,
|
||||
QoSOptions qos)
|
||||
IQoSProvider qosProvider)
|
||||
{
|
||||
var request = await new RequestBuilder()
|
||||
.WithHttpMethod(httpMethod)
|
||||
@ -30,7 +31,7 @@ namespace Ocelot.Request.Builder
|
||||
.WithRequestId(requestId)
|
||||
.WithCookies(cookies)
|
||||
.WithIsQos(isQos)
|
||||
.WithQos(qos)
|
||||
.WithQos(qosProvider)
|
||||
.Build();
|
||||
|
||||
return new OkResponse<Request>(request);
|
||||
|
@ -1,8 +1,8 @@
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.Requester.QoS;
|
||||
using Ocelot.Responses;
|
||||
using Ocelot.Configuration;
|
||||
|
||||
namespace Ocelot.Request.Builder
|
||||
{
|
||||
@ -17,6 +17,6 @@ namespace Ocelot.Request.Builder
|
||||
string contentType,
|
||||
RequestId.RequestId requestId,
|
||||
bool isQos,
|
||||
QoSOptions qos);
|
||||
IQoSProvider qosProvider);
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ using System.Net.Http.Headers;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Requester.QoS;
|
||||
|
||||
namespace Ocelot.Request.Builder
|
||||
{
|
||||
@ -24,7 +24,7 @@ namespace Ocelot.Request.Builder
|
||||
private IRequestCookieCollection _cookies;
|
||||
private readonly string[] _unsupportedHeaders = {"host"};
|
||||
private bool _isQos;
|
||||
private QoSOptions _qos;
|
||||
private IQoSProvider _qoSProvider;
|
||||
|
||||
public RequestBuilder WithHttpMethod(string httpMethod)
|
||||
{
|
||||
@ -80,9 +80,9 @@ namespace Ocelot.Request.Builder
|
||||
return this;
|
||||
}
|
||||
|
||||
public RequestBuilder WithQos(QoSOptions qos)
|
||||
public RequestBuilder WithQos(IQoSProvider qoSProvider)
|
||||
{
|
||||
_qos = qos;
|
||||
_qoSProvider = qoSProvider;
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -105,7 +105,7 @@ namespace Ocelot.Request.Builder
|
||||
|
||||
var cookieContainer = CreateCookieContainer(uri);
|
||||
|
||||
return new Request(httpRequestMessage, cookieContainer,_isQos,_qos);
|
||||
return new Request(httpRequestMessage, cookieContainer,_isQos, _qoSProvider);
|
||||
}
|
||||
|
||||
private Uri CreateUri()
|
||||
|
@ -1,10 +1,10 @@
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Ocelot.Infrastructure.RequestData;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware;
|
||||
using Ocelot.Request.Builder;
|
||||
using Ocelot.Requester.QoS;
|
||||
|
||||
namespace Ocelot.Request.Middleware
|
||||
{
|
||||
@ -13,15 +13,18 @@ namespace Ocelot.Request.Middleware
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly IRequestCreator _requestCreator;
|
||||
private readonly IOcelotLogger _logger;
|
||||
private readonly IQosProviderHouse _qosProviderHouse;
|
||||
|
||||
public HttpRequestBuilderMiddleware(RequestDelegate next,
|
||||
IOcelotLoggerFactory loggerFactory,
|
||||
IRequestScopedDataRepository requestScopedDataRepository,
|
||||
IRequestCreator requestCreator)
|
||||
IRequestCreator requestCreator,
|
||||
IQosProviderHouse qosProviderHouse)
|
||||
:base(requestScopedDataRepository)
|
||||
{
|
||||
_next = next;
|
||||
_requestCreator = requestCreator;
|
||||
_qosProviderHouse = qosProviderHouse;
|
||||
_logger = loggerFactory.CreateLogger<HttpRequestBuilderMiddleware>();
|
||||
}
|
||||
|
||||
@ -29,17 +32,35 @@ namespace Ocelot.Request.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
|
||||
.Build(context.Request.Method, DownstreamUrl, context.Request.Body,
|
||||
context.Request.Headers, context.Request.Cookies, context.Request.QueryString,
|
||||
context.Request.ContentType, new RequestId.RequestId(DownstreamRoute?.ReRoute?.RequestIdKey, context.TraceIdentifier),
|
||||
DownstreamRoute.ReRoute.IsQos,DownstreamRoute.ReRoute.QosOptions);
|
||||
.Build(context.Request.Method,
|
||||
DownstreamUrl,
|
||||
context.Request.Body,
|
||||
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)
|
||||
{
|
||||
_logger.LogDebug("IRequestCreator returned an error, setting pipeline error");
|
||||
|
||||
SetPipelineError(buildResult.Errors);
|
||||
|
||||
return;
|
||||
}
|
||||
_logger.LogDebug("setting upstream request");
|
||||
|
@ -2,22 +2,27 @@
|
||||
using Ocelot.Values;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using Ocelot.Requester.QoS;
|
||||
|
||||
namespace Ocelot.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;
|
||||
CookieContainer = cookieContainer;
|
||||
IsQos = isQos;
|
||||
Qos = qos;
|
||||
QosProvider = qosProvider;
|
||||
}
|
||||
|
||||
public HttpRequestMessage HttpRequestMessage { get; private set; }
|
||||
public CookieContainer CookieContainer { 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 Ocelot.Logging;
|
||||
using Ocelot.Values;
|
||||
using Polly.Timeout;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Requester.QoS;
|
||||
|
||||
namespace Ocelot.Requester
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
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();
|
||||
delegatingHandler.InnerHandler = httpMessageHandler;
|
||||
|
@ -1,17 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Ocelot.Errors;
|
||||
using Ocelot.Responses;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Responses;
|
||||
using Polly.CircuitBreaker;
|
||||
using Polly.Timeout;
|
||||
|
||||
namespace Ocelot.Requester
|
||||
{
|
||||
public class HttpClientHttpRequester : IHttpRequester
|
||||
{
|
||||
private readonly IOcelotLogger _logger;
|
||||
|
||||
|
||||
public HttpClientHttpRequester(IOcelotLoggerFactory loggerFactory)
|
||||
{
|
||||
_logger = loggerFactory.CreateLogger<HttpClientHttpRequester>();
|
||||
@ -19,14 +19,15 @@ namespace Ocelot.Requester
|
||||
|
||||
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 })
|
||||
{
|
||||
if (request.IsQos)
|
||||
{
|
||||
builder.WithCircuitBreaker(request.Qos, _logger, handler);
|
||||
builder.WithQoS(request.QosProvider, _logger, handler);
|
||||
}
|
||||
|
||||
using (var httpClient = builder.Build(handler))
|
||||
{
|
||||
try
|
||||
@ -34,13 +35,19 @@ namespace Ocelot.Requester
|
||||
var response = await httpClient.SendAsync(request.HttpRequestMessage);
|
||||
return new OkResponse<HttpResponseMessage>(response);
|
||||
}
|
||||
catch (Exception exception)
|
||||
catch (TimeoutRejectedException exception)
|
||||
{
|
||||
return
|
||||
new ErrorResponse<HttpResponseMessage>(new List<Error>
|
||||
{
|
||||
new UnableToCompleteRequestError(exception)
|
||||
});
|
||||
new ErrorResponse<HttpResponseMessage>(new RequestTimedOutError(exception));
|
||||
}
|
||||
catch (BrokenCircuitException 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);
|
||||
}
|
||||
|
||||
if (errors.Any(e => e.Code == OcelotErrorCode.RequestTimedOutError))
|
||||
{
|
||||
return new OkResponse<int>(503);
|
||||
}
|
||||
|
||||
return new OkResponse<int>(404);
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,13 @@ namespace Ocelot.Responses
|
||||
{
|
||||
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
|
||||
{
|
||||
public class DownstreamPathTemplate
|
||||
public class PathTemplate
|
||||
{
|
||||
public DownstreamPathTemplate(string value)
|
||||
public PathTemplate(string value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
Reference in New Issue
Block a user