mirror of
				https://github.com/nsnail/Ocelot.git
				synced 2025-11-04 15:30:49 +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