mirror of
				https://github.com/nsnail/Ocelot.git
				synced 2025-11-04 09:15:27 +08:00 
			
		
		
		
	Merge branch 'develop' into CircuitBreakerPattern
This commit is contained in:
		@@ -6,6 +6,7 @@ namespace Ocelot.Configuration.Builder
 | 
			
		||||
{
 | 
			
		||||
    public class ReRouteBuilder
 | 
			
		||||
    {
 | 
			
		||||
        private string _loadBalancerKey;
 | 
			
		||||
        private string _downstreamPathTemplate;
 | 
			
		||||
        private string _upstreamTemplate;
 | 
			
		||||
        private string _upstreamTemplatePattern;
 | 
			
		||||
@@ -35,12 +36,22 @@ namespace Ocelot.Configuration.Builder
 | 
			
		||||
        private int _exceptionsAllowedBeforeBreaking;
 | 
			
		||||
        private int _durationOfBreak;
 | 
			
		||||
        private int _timeoutValue;
 | 
			
		||||
        private string _loadBalancer;
 | 
			
		||||
        private string _serviceProviderHost;
 | 
			
		||||
        private int _serviceProviderPort;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public ReRouteBuilder()
 | 
			
		||||
        {
 | 
			
		||||
            _additionalScopes = new List<string>();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public ReRouteBuilder WithLoadBalancer(string loadBalancer)
 | 
			
		||||
        {
 | 
			
		||||
            _loadBalancer = loadBalancer;
 | 
			
		||||
            return this;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public ReRouteBuilder WithDownstreamScheme(string downstreamScheme)
 | 
			
		||||
        {
 | 
			
		||||
            _downstreamScheme = downstreamScheme;
 | 
			
		||||
@@ -213,16 +224,33 @@ namespace Ocelot.Configuration.Builder
 | 
			
		||||
            return this;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public ReRouteBuilder WithLoadBalancerKey(string loadBalancerKey)
 | 
			
		||||
        {
 | 
			
		||||
            _loadBalancerKey = loadBalancerKey;
 | 
			
		||||
            return this;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public ReRouteBuilder WithServiceProviderHost(string serviceProviderHost)
 | 
			
		||||
        {
 | 
			
		||||
            _serviceProviderHost = serviceProviderHost;
 | 
			
		||||
            return this;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public ReRouteBuilder WithServiceProviderPort(int serviceProviderPort)
 | 
			
		||||
        {
 | 
			
		||||
            _serviceProviderPort = serviceProviderPort;
 | 
			
		||||
            return this;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public ReRoute Build()
 | 
			
		||||
        {
 | 
			
		||||
            Func<HostAndPort> downstreamHostFunc = () => new HostAndPort(_downstreamHost, _dsPort);
 | 
			
		||||
 | 
			
		||||
            return new ReRoute(new DownstreamPathTemplate(_downstreamPathTemplate), _upstreamTemplate, _upstreamHttpMethod, _upstreamTemplatePattern, 
 | 
			
		||||
                _isAuthenticated, new AuthenticationOptions(_authenticationProvider, _authenticationProviderUrl, _scopeName, 
 | 
			
		||||
                _requireHttps, _additionalScopes, _scopeSecret), _configHeaderExtractorProperties, _claimToClaims, _routeClaimRequirement, 
 | 
			
		||||
                _isAuthorised, _claimToQueries, _requestIdHeaderKey, _isCached, _fileCacheOptions, _serviceName, 
 | 
			
		||||
                _useServiceDiscovery, _serviceDiscoveryAddress, _serviceDiscoveryProvider, downstreamHostFunc, _downstreamScheme,
 | 
			
		||||
                _exceptionsAllowedBeforeBreaking,_durationOfBreak, _timeoutValue);
 | 
			
		||||
                _isAuthorised, _claimToQueries, _requestIdHeaderKey, _isCached, _fileCacheOptions, _downstreamScheme, _loadBalancer,
 | 
			
		||||
                _downstreamHost, _dsPort, _loadBalancerKey, new ServiceProviderConfiguraion(_serviceName, _downstreamHost, _dsPort, _useServiceDiscovery, _serviceDiscoveryProvider, _serviceProviderHost, _serviceProviderPort),
 | 
			
		||||
                 _exceptionsAllowedBeforeBreaking,_durationOfBreak, _timeoutValue);
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,13 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Microsoft.Extensions.Logging;
 | 
			
		||||
using Microsoft.Extensions.Options;
 | 
			
		||||
using Ocelot.Configuration.File;
 | 
			
		||||
using Ocelot.Configuration.Parser;
 | 
			
		||||
using Ocelot.Configuration.Validator;
 | 
			
		||||
using Ocelot.LoadBalancer.LoadBalancers;
 | 
			
		||||
using Ocelot.Responses;
 | 
			
		||||
using Ocelot.Utilities;
 | 
			
		||||
using Ocelot.Values;
 | 
			
		||||
@@ -25,22 +27,28 @@ namespace Ocelot.Configuration.Creator
 | 
			
		||||
 | 
			
		||||
        private readonly IClaimToThingConfigurationParser _claimToThingConfigurationParser;
 | 
			
		||||
        private readonly ILogger<FileOcelotConfigurationCreator> _logger;
 | 
			
		||||
        private readonly ILoadBalancerFactory _loadBalanceFactory;
 | 
			
		||||
        private readonly ILoadBalancerHouse _loadBalancerHouse;
 | 
			
		||||
 | 
			
		||||
        public FileOcelotConfigurationCreator(
 | 
			
		||||
            IOptions<FileConfiguration> options, 
 | 
			
		||||
            IConfigurationValidator configurationValidator, 
 | 
			
		||||
            IClaimToThingConfigurationParser claimToThingConfigurationParser, 
 | 
			
		||||
            ILogger<FileOcelotConfigurationCreator> logger)
 | 
			
		||||
            ILogger<FileOcelotConfigurationCreator> logger,
 | 
			
		||||
            ILoadBalancerFactory loadBalancerFactory,
 | 
			
		||||
            ILoadBalancerHouse loadBalancerHouse)
 | 
			
		||||
        {
 | 
			
		||||
            _loadBalanceFactory = loadBalancerFactory;
 | 
			
		||||
            _loadBalancerHouse = loadBalancerHouse;
 | 
			
		||||
            _options = options;
 | 
			
		||||
            _configurationValidator = configurationValidator;
 | 
			
		||||
            _claimToThingConfigurationParser = claimToThingConfigurationParser;
 | 
			
		||||
            _logger = logger;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public Response<IOcelotConfiguration> Create()
 | 
			
		||||
        public async Task<Response<IOcelotConfiguration>> Create()
 | 
			
		||||
        {     
 | 
			
		||||
            var config = SetUpConfiguration();
 | 
			
		||||
            var config = await SetUpConfiguration();
 | 
			
		||||
 | 
			
		||||
            return new OkResponse<IOcelotConfiguration>(config);
 | 
			
		||||
        }
 | 
			
		||||
@@ -49,7 +57,7 @@ namespace Ocelot.Configuration.Creator
 | 
			
		||||
        /// This method is meant to be tempoary to convert a config to an ocelot config...probably wont keep this but we will see
 | 
			
		||||
        /// will need a refactor at some point as its crap
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        private IOcelotConfiguration SetUpConfiguration()
 | 
			
		||||
        private async Task<IOcelotConfiguration> SetUpConfiguration()
 | 
			
		||||
        {
 | 
			
		||||
            var response = _configurationValidator.IsValid(_options.Value);
 | 
			
		||||
 | 
			
		||||
@@ -69,64 +77,87 @@ namespace Ocelot.Configuration.Creator
 | 
			
		||||
 | 
			
		||||
            foreach (var reRoute in _options.Value.ReRoutes)
 | 
			
		||||
            {
 | 
			
		||||
                var ocelotReRoute = SetUpReRoute(reRoute, _options.Value.GlobalConfiguration);
 | 
			
		||||
                var ocelotReRoute = await SetUpReRoute(reRoute, _options.Value.GlobalConfiguration);
 | 
			
		||||
                reRoutes.Add(ocelotReRoute);
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            return new OcelotConfiguration(reRoutes);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private ReRoute SetUpReRoute(FileReRoute reRoute, FileGlobalConfiguration globalConfiguration)
 | 
			
		||||
        private async Task<ReRoute> SetUpReRoute(FileReRoute fileReRoute, FileGlobalConfiguration globalConfiguration)
 | 
			
		||||
        {
 | 
			
		||||
            var globalRequestIdConfiguration = !string.IsNullOrEmpty(globalConfiguration?.RequestIdKey);
 | 
			
		||||
 | 
			
		||||
            var upstreamTemplate = BuildUpstreamTemplate(reRoute);
 | 
			
		||||
            var upstreamTemplate = BuildUpstreamTemplate(fileReRoute);
 | 
			
		||||
 | 
			
		||||
            var isAuthenticated = !string.IsNullOrEmpty(reRoute.AuthenticationOptions?.Provider);
 | 
			
		||||
            var isAuthenticated = !string.IsNullOrEmpty(fileReRoute.AuthenticationOptions?.Provider);
 | 
			
		||||
 | 
			
		||||
            var isAuthorised = reRoute.RouteClaimsRequirement?.Count > 0;
 | 
			
		||||
            var isAuthorised = fileReRoute.RouteClaimsRequirement?.Count > 0;
 | 
			
		||||
 | 
			
		||||
            var isCached = reRoute.FileCacheOptions.TtlSeconds > 0;
 | 
			
		||||
            var isCached = fileReRoute.FileCacheOptions.TtlSeconds > 0;
 | 
			
		||||
 | 
			
		||||
            var requestIdKey = globalRequestIdConfiguration
 | 
			
		||||
                ? globalConfiguration.RequestIdKey
 | 
			
		||||
                : reRoute.RequestIdKey;
 | 
			
		||||
                : fileReRoute.RequestIdKey;
 | 
			
		||||
 | 
			
		||||
            var useServiceDiscovery = !string.IsNullOrEmpty(reRoute.ServiceName)
 | 
			
		||||
                && !string.IsNullOrEmpty(globalConfiguration?.ServiceDiscoveryProvider?.Address)
 | 
			
		||||
            var useServiceDiscovery = !string.IsNullOrEmpty(fileReRoute.ServiceName)
 | 
			
		||||
                && !string.IsNullOrEmpty(globalConfiguration?.ServiceDiscoveryProvider?.Provider);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            Func<HostAndPort> downstreamHostAndPortFunc = () => new HostAndPort(reRoute.DownstreamHost.Trim('/'), reRoute.DownstreamPort);
 | 
			
		||||
 | 
			
		||||
             //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.UpstreamTemplate}{fileReRoute.UpstreamHttpMethod}";
 | 
			
		||||
 | 
			
		||||
            ReRoute reRoute;
 | 
			
		||||
 
 | 
			
		||||
 | 
			
		||||
            var serviceProviderPort = globalConfiguration?.ServiceDiscoveryProvider?.Port ?? 0;
 | 
			
		||||
 | 
			
		||||
            var serviceProviderConfiguration = new ServiceProviderConfiguraion(fileReRoute.ServiceName,
 | 
			
		||||
                fileReRoute.DownstreamHost, fileReRoute.DownstreamPort, useServiceDiscovery,
 | 
			
		||||
                globalConfiguration?.ServiceDiscoveryProvider?.Provider, globalConfiguration?.ServiceDiscoveryProvider?.Host,
 | 
			
		||||
                serviceProviderPort);
 | 
			
		||||
 | 
			
		||||
            if (isAuthenticated)
 | 
			
		||||
            {
 | 
			
		||||
                var authOptionsForRoute = new AuthenticationOptions(reRoute.AuthenticationOptions.Provider,
 | 
			
		||||
                    reRoute.AuthenticationOptions.ProviderRootUrl, reRoute.AuthenticationOptions.ScopeName,
 | 
			
		||||
                    reRoute.AuthenticationOptions.RequireHttps, reRoute.AuthenticationOptions.AdditionalScopes,
 | 
			
		||||
                    reRoute.AuthenticationOptions.ScopeSecret);
 | 
			
		||||
                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(reRoute.AddHeadersToRequest);
 | 
			
		||||
                var claimsToClaims = GetAddThingsToRequest(reRoute.AddClaimsToRequest);
 | 
			
		||||
                var claimsToQueries = GetAddThingsToRequest(reRoute.AddQueriesToRequest);
 | 
			
		||||
                var claimsToHeaders = GetAddThingsToRequest(fileReRoute.AddHeadersToRequest);
 | 
			
		||||
                var claimsToClaims = GetAddThingsToRequest(fileReRoute.AddClaimsToRequest);
 | 
			
		||||
                var claimsToQueries = GetAddThingsToRequest(fileReRoute.AddQueriesToRequest);
 | 
			
		||||
 | 
			
		||||
                return new ReRoute(new DownstreamPathTemplate(reRoute.DownstreamPathTemplate), reRoute.UpstreamTemplate,
 | 
			
		||||
                    reRoute.UpstreamHttpMethod, upstreamTemplate, isAuthenticated,
 | 
			
		||||
               reRoute = new ReRoute(new DownstreamPathTemplate(fileReRoute.DownstreamPathTemplate),
 | 
			
		||||
                    fileReRoute.UpstreamTemplate,
 | 
			
		||||
                    fileReRoute.UpstreamHttpMethod, upstreamTemplate, isAuthenticated,
 | 
			
		||||
                    authOptionsForRoute, claimsToHeaders, claimsToClaims,
 | 
			
		||||
                    reRoute.RouteClaimsRequirement, isAuthorised, claimsToQueries,
 | 
			
		||||
                    requestIdKey, isCached, new CacheOptions(reRoute.FileCacheOptions.TtlSeconds),
 | 
			
		||||
                    reRoute.ServiceName, useServiceDiscovery, globalConfiguration?.ServiceDiscoveryProvider?.Provider,
 | 
			
		||||
                    globalConfiguration?.ServiceDiscoveryProvider?.Address, downstreamHostAndPortFunc, reRoute.DownstreamScheme,
 | 
			
		||||
                    reRoute.ExceptionsAllowedBeforeBreaking, reRoute.DurationOfBreak, reRoute.TimeoutValue);
 | 
			
		||||
                    fileReRoute.RouteClaimsRequirement, isAuthorised, claimsToQueries,
 | 
			
		||||
                    requestIdKey, isCached, new CacheOptions(fileReRoute.FileCacheOptions.TtlSeconds)
 | 
			
		||||
                    , fileReRoute.DownstreamScheme,
 | 
			
		||||
                    fileReRoute.LoadBalancer, fileReRoute.DownstreamHost, fileReRoute.DownstreamPort, loadBalancerKey,
 | 
			
		||||
                    serviceProviderConfiguration, serviceProviderConfiguration ,reRoute.ExceptionsAllowedBeforeBreaking,
 | 
			
		||||
                    reRoute.DurationOfBreak, reRoute.TimeoutValue);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return new ReRoute(new DownstreamPathTemplate(reRoute.DownstreamPathTemplate), reRoute.UpstreamTemplate, 
 | 
			
		||||
                reRoute.UpstreamHttpMethod, upstreamTemplate, isAuthenticated, 
 | 
			
		||||
                null, new List<ClaimToThing>(), new List<ClaimToThing>(), 
 | 
			
		||||
                reRoute.RouteClaimsRequirement, isAuthorised, new List<ClaimToThing>(),
 | 
			
		||||
                    requestIdKey, isCached, new CacheOptions(reRoute.FileCacheOptions.TtlSeconds),
 | 
			
		||||
                    reRoute.ServiceName, useServiceDiscovery, globalConfiguration?.ServiceDiscoveryProvider?.Provider,
 | 
			
		||||
                    globalConfiguration?.ServiceDiscoveryProvider?.Address, downstreamHostAndPortFunc, reRoute.DownstreamScheme,
 | 
			
		||||
                    reRoute.ExceptionsAllowedBeforeBreaking, reRoute.DurationOfBreak, reRoute.TimeoutValue);
 | 
			
		||||
            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, serviceProviderConfiguration ,reRoute.ExceptionsAllowedBeforeBreaking,
 | 
			
		||||
                    reRoute.DurationOfBreak, reRoute.TimeoutValue);
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            var loadBalancer = await _loadBalanceFactory.Get(reRoute);
 | 
			
		||||
            _loadBalancerHouse.Add(reRoute.LoadBalancerKey, loadBalancer);
 | 
			
		||||
            return reRoute;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private string BuildUpstreamTemplate(FileReRoute reRoute)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,10 @@
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Ocelot.Responses;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.Configuration.Creator
 | 
			
		||||
{
 | 
			
		||||
    public interface IOcelotConfigurationCreator
 | 
			
		||||
    {
 | 
			
		||||
        Response<IOcelotConfiguration> Create();
 | 
			
		||||
        Task<Response<IOcelotConfiguration>> Create();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -32,5 +32,6 @@ namespace Ocelot.Configuration.File
 | 
			
		||||
        public int ExceptionsAllowedBeforeBreaking { get; set; }
 | 
			
		||||
        public int DurationOfBreak { get; set; }
 | 
			
		||||
        public int TimeoutValue { get; set; }
 | 
			
		||||
        public string LoadBalancer {get;set;}
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -3,6 +3,7 @@ namespace Ocelot.Configuration.File
 | 
			
		||||
    public class FileServiceDiscoveryProvider
 | 
			
		||||
    {
 | 
			
		||||
        public string Provider {get;set;}
 | 
			
		||||
        public string Address {get;set;}
 | 
			
		||||
        public string Host {get;set;}
 | 
			
		||||
        public int Port { get; set; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,9 +1,10 @@
 | 
			
		||||
using Ocelot.Responses;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Ocelot.Responses;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.Configuration.Provider
 | 
			
		||||
{
 | 
			
		||||
    public interface IOcelotConfigurationProvider
 | 
			
		||||
    {
 | 
			
		||||
        Response<IOcelotConfiguration> Get();
 | 
			
		||||
        Task<Response<IOcelotConfiguration>> Get();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
using Ocelot.Configuration.Creator;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Ocelot.Configuration.Creator;
 | 
			
		||||
using Ocelot.Configuration.Repository;
 | 
			
		||||
using Ocelot.Responses;
 | 
			
		||||
 | 
			
		||||
@@ -19,7 +20,7 @@ namespace Ocelot.Configuration.Provider
 | 
			
		||||
            _creator = creator;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public Response<IOcelotConfiguration> Get()
 | 
			
		||||
        public async Task<Response<IOcelotConfiguration>> Get()
 | 
			
		||||
        {
 | 
			
		||||
            var repoConfig = _repo.Get();
 | 
			
		||||
 | 
			
		||||
@@ -30,7 +31,7 @@ namespace Ocelot.Configuration.Provider
 | 
			
		||||
 | 
			
		||||
            if (repoConfig.Data == null)
 | 
			
		||||
            {
 | 
			
		||||
                var creatorConfig = _creator.Create();
 | 
			
		||||
                var creatorConfig = await _creator.Create();
 | 
			
		||||
 | 
			
		||||
                if (creatorConfig.IsError)
 | 
			
		||||
                {
 | 
			
		||||
 
 | 
			
		||||
@@ -6,13 +6,24 @@ namespace Ocelot.Configuration
 | 
			
		||||
{
 | 
			
		||||
    public class ReRoute
 | 
			
		||||
    {
 | 
			
		||||
        public ReRoute(DownstreamPathTemplate downstreamPathTemplate, string upstreamTemplate, string upstreamHttpMethod, string upstreamTemplatePattern, 
 | 
			
		||||
            bool isAuthenticated, AuthenticationOptions authenticationOptions, List<ClaimToThing> configurationHeaderExtractorProperties, 
 | 
			
		||||
            List<ClaimToThing> claimsToClaims, Dictionary<string, string> routeClaimsRequirement, bool isAuthorised, List<ClaimToThing> claimsToQueries, 
 | 
			
		||||
            string requestIdKey, bool isCached, CacheOptions fileCacheOptions, string serviceName, bool useServiceDiscovery,
 | 
			
		||||
            string serviceDiscoveryProvider, string serviceDiscoveryAddress, Func<HostAndPort> downstreamHostAndPort, string downstreamScheme,
 | 
			
		||||
        public ReRoute(DownstreamPathTemplate downstreamPathTemplate, 
 | 
			
		||||
            string upstreamTemplate, string upstreamHttpMethod, 
 | 
			
		||||
            string upstreamTemplatePattern, 
 | 
			
		||||
            bool isAuthenticated, AuthenticationOptions authenticationOptions, 
 | 
			
		||||
            List<ClaimToThing> configurationHeaderExtractorProperties, 
 | 
			
		||||
            List<ClaimToThing> claimsToClaims, 
 | 
			
		||||
            Dictionary<string, string> routeClaimsRequirement, bool isAuthorised, 
 | 
			
		||||
            List<ClaimToThing> claimsToQueries, 
 | 
			
		||||
            string requestIdKey, bool isCached, CacheOptions fileCacheOptions, 
 | 
			
		||||
            string downstreamScheme, string loadBalancer, string downstreamHost, 
 | 
			
		||||
            int downstreamPort, string loadBalancerKey, ServiceProviderConfiguraion serviceProviderConfiguraion,
 | 
			
		||||
            int exceptionsAllowedBeforeBreaking =3, int durationofBreak =8, int timeoutValue = 5000)
 | 
			
		||||
        {
 | 
			
		||||
            LoadBalancerKey = loadBalancerKey;
 | 
			
		||||
            ServiceProviderConfiguraion = serviceProviderConfiguraion;
 | 
			
		||||
            LoadBalancer = loadBalancer;
 | 
			
		||||
            DownstreamHost = downstreamHost;
 | 
			
		||||
            DownstreamPort = downstreamPort;
 | 
			
		||||
            DownstreamPathTemplate = downstreamPathTemplate;
 | 
			
		||||
            UpstreamTemplate = upstreamTemplate;
 | 
			
		||||
            UpstreamHttpMethod = upstreamHttpMethod;
 | 
			
		||||
@@ -30,17 +41,13 @@ namespace Ocelot.Configuration
 | 
			
		||||
                ?? new List<ClaimToThing>();
 | 
			
		||||
            ClaimsToHeaders = configurationHeaderExtractorProperties 
 | 
			
		||||
                ?? new List<ClaimToThing>();
 | 
			
		||||
                ServiceName = serviceName;
 | 
			
		||||
                UseServiceDiscovery = useServiceDiscovery;
 | 
			
		||||
                ServiceDiscoveryProvider = serviceDiscoveryProvider;
 | 
			
		||||
                ServiceDiscoveryAddress = serviceDiscoveryAddress;
 | 
			
		||||
                DownstreamHostAndPort = downstreamHostAndPort;
 | 
			
		||||
                DownstreamScheme = downstreamScheme;
 | 
			
		||||
            ExceptionsAllowedBeforeBreaking = exceptionsAllowedBeforeBreaking;
 | 
			
		||||
            DurationOfBreak = durationofBreak;
 | 
			
		||||
            TimeoutValue = timeoutValue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public string LoadBalancerKey {get;private set;}
 | 
			
		||||
        public DownstreamPathTemplate DownstreamPathTemplate { get; private set; }
 | 
			
		||||
        public string UpstreamTemplate { get; private set; }
 | 
			
		||||
        public string UpstreamTemplatePattern { get; private set; }
 | 
			
		||||
@@ -55,14 +62,13 @@ namespace Ocelot.Configuration
 | 
			
		||||
        public string RequestIdKey { get; private set; }
 | 
			
		||||
        public bool IsCached { get; private set; }
 | 
			
		||||
        public CacheOptions FileCacheOptions { get; private set; }
 | 
			
		||||
        public string ServiceName { get; private set;}
 | 
			
		||||
        public bool UseServiceDiscovery { get; private set;}
 | 
			
		||||
        public string ServiceDiscoveryProvider { get; private set;}
 | 
			
		||||
        public string ServiceDiscoveryAddress { get; private set;}
 | 
			
		||||
        public Func<HostAndPort> DownstreamHostAndPort {get;private set;}
 | 
			
		||||
        public string DownstreamScheme {get;private set;}
 | 
			
		||||
        public int ExceptionsAllowedBeforeBreaking { get; private set; }
 | 
			
		||||
        public int DurationOfBreak { get; private set; }
 | 
			
		||||
        public int TimeoutValue { 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; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										25
									
								
								src/Ocelot/Configuration/ServiceProviderConfiguraion.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/Ocelot/Configuration/ServiceProviderConfiguraion.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
			
		||||
namespace Ocelot.Configuration
 | 
			
		||||
{
 | 
			
		||||
    public class ServiceProviderConfiguraion
 | 
			
		||||
    {
 | 
			
		||||
        public ServiceProviderConfiguraion(string serviceName, string downstreamHost,
 | 
			
		||||
            int downstreamPort, bool useServiceDiscovery, string serviceDiscoveryProvider, string serviceProviderHost, int serviceProviderPort)
 | 
			
		||||
        {
 | 
			
		||||
            ServiceName = serviceName;
 | 
			
		||||
            DownstreamHost = downstreamHost;
 | 
			
		||||
            DownstreamPort = downstreamPort;
 | 
			
		||||
            UseServiceDiscovery = useServiceDiscovery;
 | 
			
		||||
            ServiceDiscoveryProvider = serviceDiscoveryProvider;
 | 
			
		||||
            ServiceProviderHost = serviceProviderHost;
 | 
			
		||||
            ServiceProviderPort = serviceProviderPort;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public string ServiceName { get; }
 | 
			
		||||
        public string DownstreamHost { get; }
 | 
			
		||||
        public int DownstreamPort { get; }
 | 
			
		||||
        public bool UseServiceDiscovery { get; }
 | 
			
		||||
        public string ServiceDiscoveryProvider { get; }
 | 
			
		||||
        public string ServiceProviderHost { get; private set; }
 | 
			
		||||
        public int ServiceProviderPort { get; private set; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -23,11 +23,13 @@ using Ocelot.DownstreamUrlCreator.UrlTemplateReplacer;
 | 
			
		||||
using Ocelot.Headers;
 | 
			
		||||
using Ocelot.Infrastructure.Claims.Parser;
 | 
			
		||||
using Ocelot.Infrastructure.RequestData;
 | 
			
		||||
using Ocelot.LoadBalancer.LoadBalancers;
 | 
			
		||||
using Ocelot.Logging;
 | 
			
		||||
using Ocelot.QueryStrings;
 | 
			
		||||
using Ocelot.Request.Builder;
 | 
			
		||||
using Ocelot.Requester;
 | 
			
		||||
using Ocelot.Responder;
 | 
			
		||||
using Ocelot.ServiceDiscovery;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.DependencyInjection
 | 
			
		||||
{
 | 
			
		||||
@@ -59,6 +61,9 @@ namespace Ocelot.DependencyInjection
 | 
			
		||||
        {
 | 
			
		||||
            services.AddMvcCore().AddJsonFormatters();
 | 
			
		||||
            services.AddLogging();
 | 
			
		||||
            services.AddSingleton<IServiceDiscoveryProviderFactory, ServiceDiscoveryProviderFactory>();
 | 
			
		||||
            services.AddSingleton<ILoadBalancerFactory, LoadBalancerFactory>();
 | 
			
		||||
            services.AddSingleton<ILoadBalancerHouse, LoadBalancerHouse>();
 | 
			
		||||
            services.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
 | 
			
		||||
            services.AddSingleton<IUrlBuilder, UrlBuilder>();
 | 
			
		||||
            services.AddSingleton<IRemoveOutputHeaders, RemoveOutputHeaders>();
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Ocelot.Configuration.Provider;
 | 
			
		||||
using Ocelot.DownstreamRouteFinder.UrlMatcher;
 | 
			
		||||
using Ocelot.Errors;
 | 
			
		||||
@@ -21,9 +22,9 @@ namespace Ocelot.DownstreamRouteFinder.Finder
 | 
			
		||||
            _urlPathPlaceholderNameAndValueFinder = urlPathPlaceholderNameAndValueFinder;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public Response<DownstreamRoute> FindDownstreamRoute(string upstreamUrlPath, string upstreamHttpMethod)
 | 
			
		||||
        public async Task<Response<DownstreamRoute>> FindDownstreamRoute(string upstreamUrlPath, string upstreamHttpMethod)
 | 
			
		||||
        {
 | 
			
		||||
            var configuration = _configProvider.Get();
 | 
			
		||||
            var configuration = await _configProvider.Get();
 | 
			
		||||
 | 
			
		||||
            var applicableReRoutes = configuration.Data.ReRoutes.Where(r => string.Equals(r.UpstreamHttpMethod, upstreamHttpMethod, StringComparison.CurrentCultureIgnoreCase));
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,10 @@
 | 
			
		||||
using Ocelot.Responses;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Ocelot.Responses;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.DownstreamRouteFinder.Finder
 | 
			
		||||
{
 | 
			
		||||
    public interface IDownstreamRouteFinder
 | 
			
		||||
    {
 | 
			
		||||
        Response<DownstreamRoute> FindDownstreamRoute(string upstreamUrlPath, string upstreamHttpMethod);
 | 
			
		||||
        Task<Response<DownstreamRoute>> FindDownstreamRoute(string upstreamUrlPath, string upstreamHttpMethod);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -34,7 +34,7 @@ namespace Ocelot.DownstreamRouteFinder.Middleware
 | 
			
		||||
 | 
			
		||||
            _logger.LogDebug("upstream url path is {upstreamUrlPath}", upstreamUrlPath);
 | 
			
		||||
 | 
			
		||||
            var downstreamRoute = _downstreamRouteFinder.FindDownstreamRoute(upstreamUrlPath, context.Request.Method);
 | 
			
		||||
            var downstreamRoute = await _downstreamRouteFinder.FindDownstreamRoute(upstreamUrlPath, context.Request.Method);
 | 
			
		||||
 | 
			
		||||
            if (downstreamRoute.IsError)
 | 
			
		||||
            {
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,7 @@ using Ocelot.DownstreamUrlCreator.UrlTemplateReplacer;
 | 
			
		||||
using Ocelot.Infrastructure.RequestData;
 | 
			
		||||
using Ocelot.Logging;
 | 
			
		||||
using Ocelot.Middleware;
 | 
			
		||||
using Ocelot.Values;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.DownstreamUrlCreator.Middleware
 | 
			
		||||
{
 | 
			
		||||
@@ -45,8 +46,8 @@ namespace Ocelot.DownstreamUrlCreator.Middleware
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var dsScheme = DownstreamRoute.ReRoute.DownstreamScheme;
 | 
			
		||||
 | 
			
		||||
            var dsHostAndPort = DownstreamRoute.ReRoute.DownstreamHostAndPort();
 | 
			
		||||
            
 | 
			
		||||
            var dsHostAndPort = HostAndPort;
 | 
			
		||||
 | 
			
		||||
            var dsUrl = _urlBuilder.Build(dsPath.Data.Value, dsScheme, dsHostAndPort);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -41,13 +41,13 @@ namespace Ocelot.Errors.Middleware
 | 
			
		||||
 | 
			
		||||
                var message = CreateMessage(context, e);
 | 
			
		||||
                _logger.LogError(message, e);
 | 
			
		||||
                await SetInternalServerErrorOnResponse(context);
 | 
			
		||||
                SetInternalServerErrorOnResponse(context);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            _logger.LogDebug("ocelot pipeline finished");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private async Task SetInternalServerErrorOnResponse(HttpContext context)
 | 
			
		||||
        private void SetInternalServerErrorOnResponse(HttpContext context)
 | 
			
		||||
        {
 | 
			
		||||
            context.Response.OnStarting(x =>
 | 
			
		||||
            {
 | 
			
		||||
 
 | 
			
		||||
@@ -21,6 +21,10 @@
 | 
			
		||||
        DownstreamPathTemplateContainsSchemeError,
 | 
			
		||||
        DownstreamPathNullOrEmptyError,
 | 
			
		||||
        DownstreamSchemeNullOrEmptyError,
 | 
			
		||||
        DownstreamHostNullOrEmptyError
 | 
			
		||||
        DownstreamHostNullOrEmptyError,
 | 
			
		||||
        ServicesAreNullError,
 | 
			
		||||
        ServicesAreEmptyError,
 | 
			
		||||
        UnableToFindServiceDiscoveryProviderError,
 | 
			
		||||
        UnableToFindLoadBalancerError
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										24
									
								
								src/Ocelot/Infrastructure/Extensions/StringExtensions.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/Ocelot/Infrastructure/Extensions/StringExtensions.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
			
		||||
using System;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.Infrastructure.Extensions
 | 
			
		||||
{
 | 
			
		||||
    public static class StringExtensions
 | 
			
		||||
    {
 | 
			
		||||
        public static string TrimStart(this string source, string trim, StringComparison stringComparison = StringComparison.Ordinal)
 | 
			
		||||
        {
 | 
			
		||||
            if (source == null)
 | 
			
		||||
            {
 | 
			
		||||
                return null;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            string s = source;
 | 
			
		||||
            while (s.StartsWith(trim, stringComparison))
 | 
			
		||||
            {
 | 
			
		||||
                s = s.Substring(trim.Length);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return s;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										13
									
								
								src/Ocelot/LoadBalancer/LoadBalancers/ILoadBalancer.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/Ocelot/LoadBalancer/LoadBalancers/ILoadBalancer.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Ocelot.Responses;
 | 
			
		||||
using Ocelot.Values;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.LoadBalancer.LoadBalancers
 | 
			
		||||
{
 | 
			
		||||
    public interface ILoadBalancer
 | 
			
		||||
    {
 | 
			
		||||
        Task<Response<HostAndPort>> Lease();
 | 
			
		||||
        void Release(HostAndPort hostAndPort);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,10 @@
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Ocelot.Configuration;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.LoadBalancer.LoadBalancers
 | 
			
		||||
{
 | 
			
		||||
    public interface ILoadBalancerFactory
 | 
			
		||||
    {
 | 
			
		||||
        Task<ILoadBalancer> Get(ReRoute reRoute);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										10
									
								
								src/Ocelot/LoadBalancer/LoadBalancers/ILoadBalancerHouse.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/Ocelot/LoadBalancer/LoadBalancers/ILoadBalancerHouse.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
using Ocelot.Responses;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.LoadBalancer.LoadBalancers
 | 
			
		||||
{
 | 
			
		||||
    public interface ILoadBalancerHouse
 | 
			
		||||
    {
 | 
			
		||||
        Response<ILoadBalancer> Get(string key);
 | 
			
		||||
        Response Add(string key, ILoadBalancer loadBalancer);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										15
									
								
								src/Ocelot/LoadBalancer/LoadBalancers/Lease.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/Ocelot/LoadBalancer/LoadBalancers/Lease.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
using Ocelot.Values;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.LoadBalancer.LoadBalancers
 | 
			
		||||
{
 | 
			
		||||
    public class Lease
 | 
			
		||||
    {
 | 
			
		||||
        public Lease(HostAndPort hostAndPort, int connections)
 | 
			
		||||
        {
 | 
			
		||||
            HostAndPort = hostAndPort;
 | 
			
		||||
            Connections = connections;
 | 
			
		||||
        }
 | 
			
		||||
        public HostAndPort HostAndPort { get; private set; }
 | 
			
		||||
        public int Connections { get; private set; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,145 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Ocelot.Errors;
 | 
			
		||||
using Ocelot.Responses;
 | 
			
		||||
using Ocelot.Values;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.LoadBalancer.LoadBalancers
 | 
			
		||||
{
 | 
			
		||||
    public class LeastConnectionLoadBalancer : ILoadBalancer
 | 
			
		||||
    {
 | 
			
		||||
        private readonly Func<Task<List<Service>>> _services;
 | 
			
		||||
        private readonly List<Lease> _leases;
 | 
			
		||||
        private readonly string _serviceName;
 | 
			
		||||
        private static readonly object _syncLock = new object();
 | 
			
		||||
 | 
			
		||||
        public LeastConnectionLoadBalancer(Func<Task<List<Service>>> services, string serviceName)
 | 
			
		||||
        {
 | 
			
		||||
            _services = services;
 | 
			
		||||
            _serviceName = serviceName;
 | 
			
		||||
            _leases = new List<Lease>();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public async Task<Response<HostAndPort>> Lease()
 | 
			
		||||
        {
 | 
			
		||||
            var services = await _services.Invoke();
 | 
			
		||||
 | 
			
		||||
            if (services == null)
 | 
			
		||||
            {
 | 
			
		||||
                return new ErrorResponse<HostAndPort>(new List<Error>() { new ServicesAreNullError($"services were null for {_serviceName}") });
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!services.Any())
 | 
			
		||||
            {
 | 
			
		||||
                return new ErrorResponse<HostAndPort>(new List<Error>() { new ServicesAreEmptyError($"services were empty for {_serviceName}") });
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            lock(_syncLock)
 | 
			
		||||
            {        
 | 
			
		||||
                //todo - maybe this should be moved somewhere else...? Maybe on a repeater on seperate thread? loop every second and update or something?
 | 
			
		||||
                UpdateServices(services);
 | 
			
		||||
 | 
			
		||||
                var leaseWithLeastConnections = GetLeaseWithLeastConnections();
 | 
			
		||||
 | 
			
		||||
                _leases.Remove(leaseWithLeastConnections);
 | 
			
		||||
 | 
			
		||||
                leaseWithLeastConnections = AddConnection(leaseWithLeastConnections);
 | 
			
		||||
 | 
			
		||||
                _leases.Add(leaseWithLeastConnections);
 | 
			
		||||
            
 | 
			
		||||
                return new OkResponse<HostAndPort>(new HostAndPort(leaseWithLeastConnections.HostAndPort.DownstreamHost, leaseWithLeastConnections.HostAndPort.DownstreamPort));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Release(HostAndPort hostAndPort)
 | 
			
		||||
        {
 | 
			
		||||
            lock(_syncLock)
 | 
			
		||||
            {
 | 
			
		||||
                var matchingLease = _leases.FirstOrDefault(l => l.HostAndPort.DownstreamHost == hostAndPort.DownstreamHost
 | 
			
		||||
                    && l.HostAndPort.DownstreamPort == hostAndPort.DownstreamPort);
 | 
			
		||||
 | 
			
		||||
                if (matchingLease != null)
 | 
			
		||||
                {
 | 
			
		||||
                    var replacementLease = new Lease(hostAndPort, matchingLease.Connections - 1);
 | 
			
		||||
 | 
			
		||||
                    _leases.Remove(matchingLease);
 | 
			
		||||
 | 
			
		||||
                    _leases.Add(replacementLease);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private Lease AddConnection(Lease lease)
 | 
			
		||||
        {
 | 
			
		||||
            return new Lease(lease.HostAndPort, lease.Connections + 1);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private Lease GetLeaseWithLeastConnections()
 | 
			
		||||
        {
 | 
			
		||||
            //now get the service with the least connections?
 | 
			
		||||
            Lease leaseWithLeastConnections = null;
 | 
			
		||||
 | 
			
		||||
            for (var i = 0; i < _leases.Count; i++)
 | 
			
		||||
            {
 | 
			
		||||
                if (i == 0)
 | 
			
		||||
                {
 | 
			
		||||
                    leaseWithLeastConnections = _leases[i];
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    if (_leases[i].Connections < leaseWithLeastConnections.Connections)
 | 
			
		||||
                    {
 | 
			
		||||
                        leaseWithLeastConnections = _leases[i];
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return leaseWithLeastConnections;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private Response UpdateServices(List<Service> services)
 | 
			
		||||
        {
 | 
			
		||||
            if (_leases.Count > 0)
 | 
			
		||||
            {
 | 
			
		||||
                var leasesToRemove = new List<Lease>();
 | 
			
		||||
 | 
			
		||||
                foreach (var lease in _leases)
 | 
			
		||||
                {
 | 
			
		||||
                    var match = services.FirstOrDefault(s => s.HostAndPort.DownstreamHost == lease.HostAndPort.DownstreamHost
 | 
			
		||||
                        && s.HostAndPort.DownstreamPort == lease.HostAndPort.DownstreamPort);
 | 
			
		||||
 | 
			
		||||
                    if (match == null)
 | 
			
		||||
                    {
 | 
			
		||||
                        leasesToRemove.Add(lease);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                foreach (var lease in leasesToRemove)
 | 
			
		||||
                {
 | 
			
		||||
                    _leases.Remove(lease);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                foreach (var service in services)
 | 
			
		||||
                {
 | 
			
		||||
                    var exists = _leases.FirstOrDefault(l => l.HostAndPort.ToString() == service.HostAndPort.ToString());
 | 
			
		||||
 | 
			
		||||
                    if (exists == null)
 | 
			
		||||
                    {
 | 
			
		||||
                        _leases.Add(new Lease(service.HostAndPort, 0));
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                foreach (var service in services)
 | 
			
		||||
                {
 | 
			
		||||
                    _leases.Add(new Lease(service.HostAndPort, 0));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return new OkResponse();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										39
									
								
								src/Ocelot/LoadBalancer/LoadBalancers/LoadBalancerFactory.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/Ocelot/LoadBalancer/LoadBalancers/LoadBalancerFactory.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,39 @@
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Ocelot.Configuration;
 | 
			
		||||
using Ocelot.ServiceDiscovery;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.LoadBalancer.LoadBalancers
 | 
			
		||||
{
 | 
			
		||||
    public class LoadBalancerFactory : ILoadBalancerFactory
 | 
			
		||||
    {
 | 
			
		||||
        private readonly IServiceDiscoveryProviderFactory _serviceProviderFactory;
 | 
			
		||||
        public LoadBalancerFactory(IServiceDiscoveryProviderFactory serviceProviderFactory)
 | 
			
		||||
        {
 | 
			
		||||
            _serviceProviderFactory = serviceProviderFactory;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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);
 | 
			
		||||
 | 
			
		||||
            switch (reRoute.LoadBalancer)
 | 
			
		||||
            {
 | 
			
		||||
                case "RoundRobin":
 | 
			
		||||
                    return new RoundRobinLoadBalancer(await serviceProvider.Get());
 | 
			
		||||
                case "LeastConnection":
 | 
			
		||||
                    return new LeastConnectionLoadBalancer(async () => await serviceProvider.Get(), reRoute.ServiceProviderConfiguraion.ServiceName);
 | 
			
		||||
                default:
 | 
			
		||||
                    return new NoLoadBalancer(await serviceProvider.Get());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										45
									
								
								src/Ocelot/LoadBalancer/LoadBalancers/LoadBalancerHouse.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								src/Ocelot/LoadBalancer/LoadBalancers/LoadBalancerHouse.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,45 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Ocelot.Responses;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.LoadBalancer.LoadBalancers
 | 
			
		||||
{
 | 
			
		||||
    public class LoadBalancerHouse : ILoadBalancerHouse
 | 
			
		||||
    {
 | 
			
		||||
        private readonly Dictionary<string, ILoadBalancer> _loadBalancers;
 | 
			
		||||
 | 
			
		||||
        public LoadBalancerHouse()
 | 
			
		||||
        {
 | 
			
		||||
            _loadBalancers = new Dictionary<string, ILoadBalancer>();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public Response<ILoadBalancer> Get(string key)
 | 
			
		||||
        {
 | 
			
		||||
            ILoadBalancer loadBalancer;
 | 
			
		||||
 | 
			
		||||
            if(_loadBalancers.TryGetValue(key, out loadBalancer))
 | 
			
		||||
            {
 | 
			
		||||
                return new OkResponse<ILoadBalancer>(_loadBalancers[key]);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
                return new ErrorResponse<ILoadBalancer>(new List<Ocelot.Errors.Error>()
 | 
			
		||||
            {
 | 
			
		||||
                new UnableToFindLoadBalancerError($"unabe to find load balancer for {key}")
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public Response Add(string key, ILoadBalancer loadBalancer)
 | 
			
		||||
        {
 | 
			
		||||
            if (!_loadBalancers.ContainsKey(key))
 | 
			
		||||
            {
 | 
			
		||||
                _loadBalancers.Add(key, loadBalancer);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            _loadBalancers.Remove(key);
 | 
			
		||||
            _loadBalancers.Add(key, loadBalancer);
 | 
			
		||||
            return new OkResponse();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										28
									
								
								src/Ocelot/LoadBalancer/LoadBalancers/NoLoadBalancer.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/Ocelot/LoadBalancer/LoadBalancers/NoLoadBalancer.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Ocelot.Responses;
 | 
			
		||||
using Ocelot.Values;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.LoadBalancer.LoadBalancers
 | 
			
		||||
{
 | 
			
		||||
    public class NoLoadBalancer : ILoadBalancer
 | 
			
		||||
    {
 | 
			
		||||
        private readonly List<Service> _services;
 | 
			
		||||
 | 
			
		||||
        public NoLoadBalancer(List<Service> services)
 | 
			
		||||
        {
 | 
			
		||||
            _services = services;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public async Task<Response<HostAndPort>> Lease()
 | 
			
		||||
        {
 | 
			
		||||
            var service = await Task.FromResult(_services.FirstOrDefault());
 | 
			
		||||
            return new OkResponse<HostAndPort>(service.HostAndPort);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Release(HostAndPort hostAndPort)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,34 @@
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Ocelot.Responses;
 | 
			
		||||
using Ocelot.Values;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.LoadBalancer.LoadBalancers
 | 
			
		||||
{
 | 
			
		||||
    public class RoundRobinLoadBalancer : ILoadBalancer
 | 
			
		||||
    {
 | 
			
		||||
        private readonly List<Service> _services;
 | 
			
		||||
        private int _last;
 | 
			
		||||
 | 
			
		||||
        public RoundRobinLoadBalancer(List<Service> services)
 | 
			
		||||
        {
 | 
			
		||||
            _services = services;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public async Task<Response<HostAndPort>> Lease()
 | 
			
		||||
        {
 | 
			
		||||
            if (_last >= _services.Count)
 | 
			
		||||
            {
 | 
			
		||||
                _last = 0;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var next = await Task.FromResult(_services[_last]);
 | 
			
		||||
            _last++;
 | 
			
		||||
            return new OkResponse<HostAndPort>(next.HostAndPort);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Release(HostAndPort hostAndPort)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,12 @@
 | 
			
		||||
using Ocelot.Errors;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.LoadBalancer.LoadBalancers
 | 
			
		||||
{
 | 
			
		||||
    public class ServicesAreEmptyError : Error
 | 
			
		||||
    {
 | 
			
		||||
        public ServicesAreEmptyError(string message)
 | 
			
		||||
            : base(message, OcelotErrorCode.ServicesAreEmptyError)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,12 @@
 | 
			
		||||
using Ocelot.Errors;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.LoadBalancer.LoadBalancers
 | 
			
		||||
{
 | 
			
		||||
    public class ServicesAreNullError : Error
 | 
			
		||||
    {
 | 
			
		||||
        public ServicesAreNullError(string message)
 | 
			
		||||
            : base(message, OcelotErrorCode.ServicesAreNullError)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,12 @@
 | 
			
		||||
using Ocelot.Errors;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.LoadBalancer.LoadBalancers
 | 
			
		||||
{
 | 
			
		||||
    public class UnableToFindLoadBalancerError : Errors.Error
 | 
			
		||||
    {
 | 
			
		||||
        public UnableToFindLoadBalancerError(string message) 
 | 
			
		||||
            : base(message, OcelotErrorCode.UnableToFindLoadBalancerError)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,69 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Microsoft.AspNetCore.Http;
 | 
			
		||||
using Ocelot.Infrastructure.RequestData;
 | 
			
		||||
using Ocelot.LoadBalancer.LoadBalancers;
 | 
			
		||||
using Ocelot.Logging;
 | 
			
		||||
using Ocelot.Middleware;
 | 
			
		||||
using Ocelot.QueryStrings.Middleware;
 | 
			
		||||
using Ocelot.ServiceDiscovery;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.LoadBalancer.Middleware
 | 
			
		||||
{
 | 
			
		||||
    public class LoadBalancingMiddleware : OcelotMiddleware
 | 
			
		||||
    {
 | 
			
		||||
        private readonly RequestDelegate _next;
 | 
			
		||||
        private readonly IOcelotLogger _logger;
 | 
			
		||||
        private readonly ILoadBalancerHouse _loadBalancerHouse;
 | 
			
		||||
 | 
			
		||||
        public LoadBalancingMiddleware(RequestDelegate next,
 | 
			
		||||
            IOcelotLoggerFactory loggerFactory,
 | 
			
		||||
            IRequestScopedDataRepository requestScopedDataRepository,
 | 
			
		||||
            ILoadBalancerHouse loadBalancerHouse) 
 | 
			
		||||
            : base(requestScopedDataRepository)
 | 
			
		||||
        {
 | 
			
		||||
            _next = next;
 | 
			
		||||
            _logger = loggerFactory.CreateLogger<QueryStringBuilderMiddleware>();
 | 
			
		||||
            _loadBalancerHouse = loadBalancerHouse;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public async Task Invoke(HttpContext context)
 | 
			
		||||
        {
 | 
			
		||||
            _logger.LogDebug("started calling load balancing middleware");
 | 
			
		||||
 | 
			
		||||
            var loadBalancer = _loadBalancerHouse.Get(DownstreamRoute.ReRoute.LoadBalancerKey);
 | 
			
		||||
            if(loadBalancer.IsError)
 | 
			
		||||
            {
 | 
			
		||||
                SetPipelineError(loadBalancer.Errors);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var hostAndPort = await loadBalancer.Data.Lease();
 | 
			
		||||
            if(hostAndPort.IsError)
 | 
			
		||||
            { 
 | 
			
		||||
                SetPipelineError(hostAndPort.Errors);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            SetHostAndPortForThisRequest(hostAndPort.Data);
 | 
			
		||||
 | 
			
		||||
            _logger.LogDebug("calling next middleware");
 | 
			
		||||
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                await _next.Invoke(context);
 | 
			
		||||
 | 
			
		||||
                loadBalancer.Data.Release(hostAndPort.Data);
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception)
 | 
			
		||||
            {
 | 
			
		||||
                loadBalancer.Data.Release(hostAndPort.Data);
 | 
			
		||||
                 
 | 
			
		||||
                 _logger.LogDebug("error calling next middleware, exception will be thrown to global handler");
 | 
			
		||||
                throw;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            _logger.LogDebug("succesfully called next middleware");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,12 @@
 | 
			
		||||
using Microsoft.AspNetCore.Builder;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.LoadBalancer.Middleware
 | 
			
		||||
{
 | 
			
		||||
 public static class LoadBalancingMiddlewareExtensions
 | 
			
		||||
    {
 | 
			
		||||
        public static IApplicationBuilder UseLoadBalancingMiddleware(this IApplicationBuilder builder)
 | 
			
		||||
        {
 | 
			
		||||
            return builder.UseMiddleware<LoadBalancingMiddleware>();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -3,6 +3,7 @@ using System.Net.Http;
 | 
			
		||||
using Ocelot.DownstreamRouteFinder;
 | 
			
		||||
using Ocelot.Errors;
 | 
			
		||||
using Ocelot.Infrastructure.RequestData;
 | 
			
		||||
using Ocelot.Values;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.Middleware
 | 
			
		||||
{
 | 
			
		||||
@@ -69,6 +70,20 @@ namespace Ocelot.Middleware
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public HostAndPort HostAndPort 
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                var hostAndPort = _requestScopedDataRepository.Get<HostAndPort>("HostAndPort");
 | 
			
		||||
                return hostAndPort.Data;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void SetHostAndPortForThisRequest(HostAndPort hostAndPort)
 | 
			
		||||
        {
 | 
			
		||||
            _requestScopedDataRepository.Add("HostAndPort", hostAndPort);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void SetDownstreamRouteForThisRequest(DownstreamRoute downstreamRoute)
 | 
			
		||||
        {
 | 
			
		||||
            _requestScopedDataRepository.Add("DownstreamRoute", downstreamRoute);
 | 
			
		||||
 
 | 
			
		||||
@@ -18,6 +18,8 @@ namespace Ocelot.Middleware
 | 
			
		||||
    using System.Threading.Tasks;
 | 
			
		||||
    using Authorisation.Middleware;
 | 
			
		||||
    using Microsoft.AspNetCore.Http;
 | 
			
		||||
    using Ocelot.Configuration.Provider;
 | 
			
		||||
    using Ocelot.LoadBalancer.Middleware;
 | 
			
		||||
 | 
			
		||||
    public static class OcelotMiddlewareExtensions
 | 
			
		||||
    {
 | 
			
		||||
@@ -28,6 +30,7 @@ namespace Ocelot.Middleware
 | 
			
		||||
        /// <returns></returns>
 | 
			
		||||
        public static IApplicationBuilder UseOcelot(this IApplicationBuilder builder)
 | 
			
		||||
        {
 | 
			
		||||
            CreateConfiguration(builder);
 | 
			
		||||
            builder.UseOcelot(new OcelotMiddlewareConfiguration());
 | 
			
		||||
            return builder;
 | 
			
		||||
        }
 | 
			
		||||
@@ -40,6 +43,8 @@ namespace Ocelot.Middleware
 | 
			
		||||
        /// <returns></returns>
 | 
			
		||||
        public static IApplicationBuilder UseOcelot(this IApplicationBuilder builder, OcelotMiddlewareConfiguration middlewareConfiguration)
 | 
			
		||||
        {
 | 
			
		||||
            CreateConfiguration(builder);
 | 
			
		||||
            
 | 
			
		||||
            // This is registered to catch any global exceptions that are not handled
 | 
			
		||||
            builder.UseExceptionHandlerMiddleware();
 | 
			
		||||
 | 
			
		||||
@@ -98,6 +103,9 @@ namespace Ocelot.Middleware
 | 
			
		||||
            // Now we can run any query string transformation logic
 | 
			
		||||
            builder.UseQueryStringBuilderMiddleware();
 | 
			
		||||
 | 
			
		||||
            // Get the load balancer for this request
 | 
			
		||||
            builder.UseLoadBalancingMiddleware();
 | 
			
		||||
 | 
			
		||||
            // This takes the downstream route we retrieved earlier and replaces any placeholders with the variables that should be used
 | 
			
		||||
            builder.UseDownstreamUrlCreatorMiddleware();
 | 
			
		||||
 | 
			
		||||
@@ -114,6 +122,18 @@ namespace Ocelot.Middleware
 | 
			
		||||
            return builder;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private static void CreateConfiguration(IApplicationBuilder builder)
 | 
			
		||||
        {
 | 
			
		||||
            var configProvider = (IOcelotConfigurationProvider)builder.ApplicationServices.GetService(typeof(IOcelotConfigurationProvider));
 | 
			
		||||
            
 | 
			
		||||
            var config = configProvider.Get();
 | 
			
		||||
            
 | 
			
		||||
            if(config == null)
 | 
			
		||||
            {
 | 
			
		||||
                throw new Exception("Unable to start Ocelot: configuration was null");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private static void UseIfNotNull(this IApplicationBuilder builder, Func<HttpContext, Func<Task>, Task> middleware)
 | 
			
		||||
        {
 | 
			
		||||
            if (middleware != null)
 | 
			
		||||
 
 | 
			
		||||
@@ -24,7 +24,7 @@ namespace Ocelot.Responder
 | 
			
		||||
            _removeOutputHeaders = removeOutputHeaders;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public async Task<Response> SetResponseOnHttpContext(HttpContext context, HttpResponseMessage response)
 | 
			
		||||
        public async Task SetResponseOnHttpContext(HttpContext context, HttpResponseMessage response)
 | 
			
		||||
        {
 | 
			
		||||
            _removeOutputHeaders.Remove(response.Headers);
 | 
			
		||||
 | 
			
		||||
@@ -56,7 +56,6 @@ namespace Ocelot.Responder
 | 
			
		||||
            {
 | 
			
		||||
                await stream.CopyToAsync(context.Response.Body);
 | 
			
		||||
            }
 | 
			
		||||
            return new OkResponse();       
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private static void AddHeaderIfDoesntExist(HttpContext context, KeyValuePair<string, IEnumerable<string>> httpResponseHeader)
 | 
			
		||||
@@ -67,14 +66,13 @@ namespace Ocelot.Responder
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public async Task<Response> SetErrorResponseOnContext(HttpContext context, int statusCode)
 | 
			
		||||
        public void SetErrorResponseOnContext(HttpContext context, int statusCode)
 | 
			
		||||
        {
 | 
			
		||||
            context.Response.OnStarting(x =>
 | 
			
		||||
            {
 | 
			
		||||
                context.Response.StatusCode = statusCode;
 | 
			
		||||
                return Task.CompletedTask;
 | 
			
		||||
            }, context);
 | 
			
		||||
            return new OkResponse();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,13 +1,12 @@
 | 
			
		||||
using System.Net.Http;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Microsoft.AspNetCore.Http;
 | 
			
		||||
using Ocelot.Responses;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.Responder
 | 
			
		||||
{
 | 
			
		||||
    public interface IHttpResponder
 | 
			
		||||
    {
 | 
			
		||||
        Task<Response> SetResponseOnHttpContext(HttpContext context, HttpResponseMessage response);
 | 
			
		||||
        Task<Response> SetErrorResponseOnContext(HttpContext context, int statusCode);
 | 
			
		||||
        Task SetResponseOnHttpContext(HttpContext context, HttpResponseMessage response);
 | 
			
		||||
        void SetErrorResponseOnContext(HttpContext context, int statusCode);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -46,34 +46,27 @@ namespace Ocelot.Responder.Middleware
 | 
			
		||||
 | 
			
		||||
                _logger.LogDebug("received errors setting error response");
 | 
			
		||||
 | 
			
		||||
                await SetErrorResponse(context, errors);
 | 
			
		||||
                SetErrorResponse(context, errors);
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                _logger.LogDebug("no pipeline error, setting response");
 | 
			
		||||
 | 
			
		||||
                var setResponse = await _responder.SetResponseOnHttpContext(context, HttpResponseMessage);
 | 
			
		||||
 | 
			
		||||
                if (setResponse.IsError)
 | 
			
		||||
                {
 | 
			
		||||
                    _logger.LogDebug("error setting response, returning error to client");
 | 
			
		||||
 | 
			
		||||
                    await SetErrorResponse(context, setResponse.Errors);
 | 
			
		||||
                }
 | 
			
		||||
                await _responder.SetResponseOnHttpContext(context, HttpResponseMessage);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private async Task SetErrorResponse(HttpContext context, List<Error> errors)
 | 
			
		||||
        private void SetErrorResponse(HttpContext context, List<Error> errors)
 | 
			
		||||
        {
 | 
			
		||||
            var statusCode = _codeMapper.Map(errors);
 | 
			
		||||
 | 
			
		||||
            if (!statusCode.IsError)
 | 
			
		||||
            {
 | 
			
		||||
                await _responder.SetErrorResponseOnContext(context, statusCode.Data);
 | 
			
		||||
                _responder.SetErrorResponseOnContext(context, statusCode.Data);
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                await _responder.SetErrorResponseOnContext(context, 500);
 | 
			
		||||
                _responder.SetErrorResponseOnContext(context, 500);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										21
									
								
								src/Ocelot/ServiceDiscovery/ConfigurationServiceProvider.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								src/Ocelot/ServiceDiscovery/ConfigurationServiceProvider.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Ocelot.Values;
 | 
			
		||||
    
 | 
			
		||||
namespace Ocelot.ServiceDiscovery
 | 
			
		||||
{
 | 
			
		||||
    public class ConfigurationServiceProvider : IServiceDiscoveryProvider
 | 
			
		||||
    {
 | 
			
		||||
        private readonly List<Service> _services;
 | 
			
		||||
 | 
			
		||||
        public ConfigurationServiceProvider(List<Service> services)
 | 
			
		||||
        {
 | 
			
		||||
            _services = services;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public async Task<List<Service>> Get()
 | 
			
		||||
        {
 | 
			
		||||
            return await Task.FromResult(_services);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										16
									
								
								src/Ocelot/ServiceDiscovery/ConsulRegistryConfiguration.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/Ocelot/ServiceDiscovery/ConsulRegistryConfiguration.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
namespace Ocelot.ServiceDiscovery
 | 
			
		||||
{
 | 
			
		||||
    public class ConsulRegistryConfiguration
 | 
			
		||||
    {
 | 
			
		||||
        public ConsulRegistryConfiguration(string hostName, int port, string serviceName)
 | 
			
		||||
        {
 | 
			
		||||
            HostName = hostName;
 | 
			
		||||
            Port = port;
 | 
			
		||||
            ServiceName = serviceName;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public string ServiceName { get; private set; }
 | 
			
		||||
        public string HostName { get; private set; }
 | 
			
		||||
        public int Port { get; private set; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,55 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Consul;
 | 
			
		||||
using Ocelot.Infrastructure.Extensions;
 | 
			
		||||
using Ocelot.Values;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.ServiceDiscovery
 | 
			
		||||
{
 | 
			
		||||
    public class ConsulServiceDiscoveryProvider : IServiceDiscoveryProvider
 | 
			
		||||
    {
 | 
			
		||||
        private readonly ConsulRegistryConfiguration _configuration;
 | 
			
		||||
        private readonly ConsulClient _consul;
 | 
			
		||||
        private const string VersionPrefix = "version-";
 | 
			
		||||
 | 
			
		||||
        public ConsulServiceDiscoveryProvider(ConsulRegistryConfiguration consulRegistryConfiguration)
 | 
			
		||||
        {
 | 
			
		||||
            var consulHost = string.IsNullOrEmpty(consulRegistryConfiguration?.HostName) ? "localhost" : consulRegistryConfiguration.HostName;
 | 
			
		||||
            var consulPort = consulRegistryConfiguration?.Port ?? 8500;
 | 
			
		||||
            _configuration = new ConsulRegistryConfiguration(consulHost, consulPort, consulRegistryConfiguration?.ServiceName);
 | 
			
		||||
 | 
			
		||||
            _consul = new ConsulClient(config =>
 | 
			
		||||
            {
 | 
			
		||||
                config.Address = new Uri($"http://{_configuration.HostName}:{_configuration.Port}");
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public async Task<List<Service>> Get()
 | 
			
		||||
        {
 | 
			
		||||
            var queryResult = await _consul.Health.Service(_configuration.ServiceName, string.Empty, true);
 | 
			
		||||
 | 
			
		||||
            var services = queryResult.Response.Select(BuildService);
 | 
			
		||||
 | 
			
		||||
            return services.ToList();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private Service BuildService(ServiceEntry serviceEntry)
 | 
			
		||||
        {
 | 
			
		||||
            return new Service(
 | 
			
		||||
                serviceEntry.Service.Service,
 | 
			
		||||
                new HostAndPort(serviceEntry.Service.Address, serviceEntry.Service.Port),
 | 
			
		||||
                serviceEntry.Service.ID,
 | 
			
		||||
                GetVersionFromStrings(serviceEntry.Service.Tags),
 | 
			
		||||
                serviceEntry.Service.Tags ?? Enumerable.Empty<string>());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private string GetVersionFromStrings(IEnumerable<string> strings)
 | 
			
		||||
        {
 | 
			
		||||
            return strings
 | 
			
		||||
                ?.FirstOrDefault(x => x.StartsWith(VersionPrefix, StringComparison.Ordinal))
 | 
			
		||||
                .TrimStart(VersionPrefix);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								src/Ocelot/ServiceDiscovery/IServiceDiscoveryProvider.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/Ocelot/ServiceDiscovery/IServiceDiscoveryProvider.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Ocelot.Values;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.ServiceDiscovery
 | 
			
		||||
{
 | 
			
		||||
    public interface IServiceDiscoveryProvider
 | 
			
		||||
    {
 | 
			
		||||
         Task<List<Service>> Get();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,10 @@
 | 
			
		||||
using System;
 | 
			
		||||
using Ocelot.Configuration;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.ServiceDiscovery
 | 
			
		||||
{
 | 
			
		||||
    public interface IServiceDiscoveryProviderFactory
 | 
			
		||||
    {
 | 
			
		||||
        IServiceDiscoveryProvider Get(ServiceProviderConfiguraion serviceConfig);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,34 @@
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using Ocelot.Configuration;
 | 
			
		||||
using Ocelot.Values;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.ServiceDiscovery
 | 
			
		||||
{
 | 
			
		||||
    public class ServiceDiscoveryProviderFactory : IServiceDiscoveryProviderFactory
 | 
			
		||||
    {
 | 
			
		||||
        public  IServiceDiscoveryProvider Get(ServiceProviderConfiguraion serviceConfig)
 | 
			
		||||
        {
 | 
			
		||||
            if (serviceConfig.UseServiceDiscovery)
 | 
			
		||||
            {
 | 
			
		||||
                return GetServiceDiscoveryProvider(serviceConfig.ServiceName, serviceConfig.ServiceDiscoveryProvider, serviceConfig.ServiceProviderHost, serviceConfig.ServiceProviderPort);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var services = new List<Service>()
 | 
			
		||||
            {
 | 
			
		||||
                new Service(serviceConfig.ServiceName, 
 | 
			
		||||
                new HostAndPort(serviceConfig.DownstreamHost, serviceConfig.DownstreamPort),
 | 
			
		||||
                string.Empty, 
 | 
			
		||||
                string.Empty, 
 | 
			
		||||
                new string[0])
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            return new ConfigurationServiceProvider(services);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private IServiceDiscoveryProvider GetServiceDiscoveryProvider(string serviceName, string serviceProviderName, string providerHostName, int providerPort)
 | 
			
		||||
        {
 | 
			
		||||
            var consulRegistryConfiguration = new ConsulRegistryConfiguration(providerHostName, providerPort, serviceName);
 | 
			
		||||
            return new ConsulServiceDiscoveryProvider(consulRegistryConfiguration);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,12 @@
 | 
			
		||||
using Ocelot.Errors;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.ServiceDiscovery
 | 
			
		||||
{
 | 
			
		||||
    public class UnableToFindServiceDiscoveryProviderError : Error
 | 
			
		||||
    {
 | 
			
		||||
        public UnableToFindServiceDiscoveryProviderError(string message) 
 | 
			
		||||
            : base(message, OcelotErrorCode.UnableToFindServiceDiscoveryProviderError)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -4,7 +4,7 @@
 | 
			
		||||
    {
 | 
			
		||||
        public HostAndPort(string downstreamHost, int downstreamPort)
 | 
			
		||||
        {
 | 
			
		||||
            DownstreamHost = downstreamHost;
 | 
			
		||||
            DownstreamHost = downstreamHost?.Trim('/');
 | 
			
		||||
            DownstreamPort = downstreamPort;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										29
									
								
								src/Ocelot/Values/Service.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								src/Ocelot/Values/Service.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,29 @@
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.Values
 | 
			
		||||
{
 | 
			
		||||
    public class Service
 | 
			
		||||
    {
 | 
			
		||||
        public Service(string name, 
 | 
			
		||||
            HostAndPort hostAndPort, 
 | 
			
		||||
            string id, 
 | 
			
		||||
            string version, 
 | 
			
		||||
            IEnumerable<string> tags)
 | 
			
		||||
        {
 | 
			
		||||
            Name = name;
 | 
			
		||||
            HostAndPort = hostAndPort;
 | 
			
		||||
            Id = id;
 | 
			
		||||
            Version = version;
 | 
			
		||||
            Tags = tags;
 | 
			
		||||
        }
 | 
			
		||||
        public string Id { get; private set; }
 | 
			
		||||
 | 
			
		||||
        public string Name { get; private set; }
 | 
			
		||||
 | 
			
		||||
        public string Version { get; private set; }
 | 
			
		||||
 | 
			
		||||
        public IEnumerable<string> Tags { get; private set; }
 | 
			
		||||
 | 
			
		||||
        public HostAndPort HostAndPort { get; private set; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,42 +1,42 @@
 | 
			
		||||
{
 | 
			
		||||
  "version": "1.0.0-*",
 | 
			
		||||
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "Microsoft.AspNetCore.Server.IISIntegration": "1.1.0",
 | 
			
		||||
    "Microsoft.Extensions.Configuration.EnvironmentVariables": "1.1.0",
 | 
			
		||||
    "Microsoft.Extensions.Configuration.FileExtensions": "1.1.0",
 | 
			
		||||
    "Microsoft.Extensions.Configuration.Json": "1.1.0",
 | 
			
		||||
    "Microsoft.Extensions.Logging": "1.1.0",
 | 
			
		||||
    "Microsoft.Extensions.Logging.Console": "1.1.0",
 | 
			
		||||
    "Microsoft.Extensions.Logging.Debug": "1.1.0",
 | 
			
		||||
    "Microsoft.Extensions.Options.ConfigurationExtensions": "1.1.0",
 | 
			
		||||
    "Microsoft.AspNetCore.Http": "1.1.0",
 | 
			
		||||
    "System.Text.RegularExpressions": "4.3.0",
 | 
			
		||||
    "Microsoft.AspNetCore.Authentication.OAuth": "1.1.0",
 | 
			
		||||
    "Microsoft.AspNetCore.Authentication.JwtBearer": "1.1.0",
 | 
			
		||||
    "Microsoft.AspNetCore.Authentication.OpenIdConnect": "1.1.0",
 | 
			
		||||
    "Microsoft.AspNetCore.Authentication.Cookies": "1.1.0",
 | 
			
		||||
    "Microsoft.AspNetCore.Authentication.Google": "1.1.0",
 | 
			
		||||
    "Microsoft.AspNetCore.Authentication.Facebook": "1.1.0",
 | 
			
		||||
    "Microsoft.AspNetCore.Authentication.Twitter": "1.1.0",
 | 
			
		||||
    "Microsoft.AspNetCore.Authentication.MicrosoftAccount": "1.1.0",
 | 
			
		||||
    "Microsoft.AspNetCore.Authentication": "1.1.0",
 | 
			
		||||
    "IdentityServer4.AccessTokenValidation": "1.0.2",
 | 
			
		||||
    "Microsoft.AspNetCore.Mvc": "1.1.0",
 | 
			
		||||
    "Microsoft.AspNetCore.Server.Kestrel": "1.1.0",
 | 
			
		||||
    "Microsoft.NETCore.App": "1.1.0",
 | 
			
		||||
    "CacheManager.Core": "0.9.2",
 | 
			
		||||
    "CacheManager.Microsoft.Extensions.Configuration": "0.9.2",
 | 
			
		||||
    "CacheManager.Microsoft.Extensions.Logging": "0.9.2",
 | 
			
		||||
    "Polly": "5.0.3"
 | 
			
		||||
  },
 | 
			
		||||
  "version": "0.0.0-dev",
 | 
			
		||||
    "dependencies": {
 | 
			
		||||
        "Microsoft.AspNetCore.Server.IISIntegration": "1.1.0",
 | 
			
		||||
        "Microsoft.Extensions.Configuration.EnvironmentVariables": "1.1.0",
 | 
			
		||||
        "Microsoft.Extensions.Configuration.FileExtensions": "1.1.0",
 | 
			
		||||
        "Microsoft.Extensions.Configuration.Json": "1.1.0",
 | 
			
		||||
        "Microsoft.Extensions.Logging": "1.1.0",
 | 
			
		||||
        "Microsoft.Extensions.Logging.Console": "1.1.0",
 | 
			
		||||
        "Microsoft.Extensions.Logging.Debug": "1.1.0",
 | 
			
		||||
        "Microsoft.Extensions.Options.ConfigurationExtensions": "1.1.0",
 | 
			
		||||
        "Microsoft.AspNetCore.Http": "1.1.0",
 | 
			
		||||
        "System.Text.RegularExpressions": "4.3.0",
 | 
			
		||||
        "Microsoft.AspNetCore.Authentication.OAuth": "1.1.0",
 | 
			
		||||
        "Microsoft.AspNetCore.Authentication.JwtBearer": "1.1.0",
 | 
			
		||||
        "Microsoft.AspNetCore.Authentication.OpenIdConnect": "1.1.0",
 | 
			
		||||
        "Microsoft.AspNetCore.Authentication.Cookies": "1.1.0",
 | 
			
		||||
        "Microsoft.AspNetCore.Authentication.Google": "1.1.0",
 | 
			
		||||
        "Microsoft.AspNetCore.Authentication.Facebook": "1.1.0",
 | 
			
		||||
        "Microsoft.AspNetCore.Authentication.Twitter": "1.1.0",
 | 
			
		||||
        "Microsoft.AspNetCore.Authentication.MicrosoftAccount": "1.1.0",
 | 
			
		||||
        "Microsoft.AspNetCore.Authentication": "1.1.0",
 | 
			
		||||
        "IdentityServer4.AccessTokenValidation": "1.0.2",
 | 
			
		||||
        "Microsoft.AspNetCore.Mvc": "1.1.0",
 | 
			
		||||
        "Microsoft.AspNetCore.Server.Kestrel": "1.1.0",
 | 
			
		||||
        "Microsoft.NETCore.App": "1.1.0",
 | 
			
		||||
        "CacheManager.Core": "0.9.2",
 | 
			
		||||
        "CacheManager.Microsoft.Extensions.Configuration": "0.9.2",
 | 
			
		||||
        "CacheManager.Microsoft.Extensions.Logging": "0.9.2",
 | 
			
		||||
        "Consul": "0.7.2.1",
 | 
			
		||||
         "Polly": "5.0.3"
 | 
			
		||||
   },
 | 
			
		||||
  "runtimes": {
 | 
			
		||||
    "win10-x64": {},
 | 
			
		||||
    "osx.10.11-x64":{},
 | 
			
		||||
    "win7-x64": {}
 | 
			
		||||
  },
 | 
			
		||||
  "frameworks": {
 | 
			
		||||
    "netcoreapp1.4": {
 | 
			
		||||
    "netcoreapp1.1": {
 | 
			
		||||
      "imports": [
 | 
			
		||||
      ]
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user