mirror of
				https://github.com/nsnail/Ocelot.git
				synced 2025-11-04 12:50:49 +08:00 
			
		
		
		
	Merge pull request #105 from geffzhang/develop
I've applied Ocelot to the product,bug fixed
This commit is contained in:
		@@ -28,6 +28,12 @@ namespace Ocelot.Configuration.Validator
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            result = CheckForReRoutesContainingDownstreamSchemeInDownstreamPathTemplate(configuration);
 | 
					            result = CheckForReRoutesContainingDownstreamSchemeInDownstreamPathTemplate(configuration);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (result.IsError)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                return new OkResponse<ConfigurationValidationResult>(result);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            result = CheckForReRoutesRateLimitOptions(configuration);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (result.IsError)
 | 
					            if (result.IsError)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                return new OkResponse<ConfigurationValidationResult>(result);
 | 
					                return new OkResponse<ConfigurationValidationResult>(result);
 | 
				
			||||||
@@ -111,5 +117,35 @@ namespace Ocelot.Configuration.Validator
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            return new ConfigurationValidationResult(true, errors);
 | 
					            return new ConfigurationValidationResult(true, errors);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private ConfigurationValidationResult CheckForReRoutesRateLimitOptions(FileConfiguration configuration)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var errors = new List<Error>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            foreach (var reRoute in configuration.ReRoutes)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                if (reRoute.RateLimitOptions.EnableRateLimiting)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    if (!IsValidPeriod(reRoute))
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        errors.Add(new RateLimitOptionsValidationError($"{reRoute.RateLimitOptions.Period} not contains scheme"));
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (errors.Any())
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                return new ConfigurationValidationResult(true, errors);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return new ConfigurationValidationResult(false, errors);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private static bool IsValidPeriod(FileReRoute reRoute)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            string period = reRoute.RateLimitOptions.Period;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return period.Contains("s") || period.Contains("m") || period.Contains("h") || period.Contains("d");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,15 @@
 | 
				
			|||||||
 | 
					using Ocelot.Errors;
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Text;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.Configuration.Validator
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class RateLimitOptionsValidationError : Error
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public RateLimitOptionsValidationError(string message) 
 | 
				
			||||||
 | 
					            : base(message, OcelotErrorCode.RateLimitOptionsError)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -29,6 +29,7 @@
 | 
				
			|||||||
        RequestTimedOutError,
 | 
					        RequestTimedOutError,
 | 
				
			||||||
        UnableToFindQoSProviderError,
 | 
					        UnableToFindQoSProviderError,
 | 
				
			||||||
        UnableToSetConfigInConsulError,
 | 
					        UnableToSetConfigInConsulError,
 | 
				
			||||||
        UnmappableRequestError
 | 
					        UnmappableRequestError,
 | 
				
			||||||
 | 
					        RateLimitOptionsError
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,7 +19,7 @@ namespace Ocelot.LoadBalancer.LoadBalancers
 | 
				
			|||||||
            switch (reRoute.LoadBalancer)
 | 
					            switch (reRoute.LoadBalancer)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                case "RoundRobin":
 | 
					                case "RoundRobin":
 | 
				
			||||||
                    return new RoundRobinLoadBalancer(await serviceProvider.Get());
 | 
					                    return new RoundRobinLoadBalancer(async () => await serviceProvider.Get());
 | 
				
			||||||
                case "LeastConnection":
 | 
					                case "LeastConnection":
 | 
				
			||||||
                    return new LeastConnectionLoadBalancer(async () => await serviceProvider.Get(), reRoute.ServiceProviderConfiguraion.ServiceName);
 | 
					                    return new LeastConnectionLoadBalancer(async () => await serviceProvider.Get(), reRoute.ServiceProviderConfiguraion.ServiceName);
 | 
				
			||||||
                default:
 | 
					                default:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,27 +2,31 @@
 | 
				
			|||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
using Ocelot.Responses;
 | 
					using Ocelot.Responses;
 | 
				
			||||||
using Ocelot.Values;
 | 
					using Ocelot.Values;
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Ocelot.LoadBalancer.LoadBalancers
 | 
					namespace Ocelot.LoadBalancer.LoadBalancers
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public class RoundRobinLoadBalancer : ILoadBalancer
 | 
					    public class RoundRobinLoadBalancer : ILoadBalancer
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        private readonly List<Service> _services;
 | 
					        private readonly Func<Task<List<Service>>> _services;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private int _last;
 | 
					        private int _last;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public RoundRobinLoadBalancer(List<Service> services)
 | 
					        public RoundRobinLoadBalancer(Func<Task<List<Service>>> services)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _services = services;
 | 
					            _services = services;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public async Task<Response<HostAndPort>> Lease()
 | 
					        public async Task<Response<HostAndPort>> Lease()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (_last >= _services.Count)
 | 
					            var services = await _services.Invoke();
 | 
				
			||||||
 | 
					            if (_last >= services.Count)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                _last = 0;
 | 
					                _last = 0;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var next = await Task.FromResult(_services[_last]);
 | 
					            var next = await Task.FromResult(services[_last]);
 | 
				
			||||||
            _last++;
 | 
					            _last++;
 | 
				
			||||||
            return new OkResponse<HostAndPort>(next.HostAndPort);
 | 
					            return new OkResponse<HostAndPort>(next.HostAndPort);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,6 +7,7 @@ using System.Threading.Tasks;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace Ocelot.RateLimit
 | 
					namespace Ocelot.RateLimit
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public class ClientRateLimitProcessor
 | 
					    public class ClientRateLimitProcessor
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        private readonly IRateLimitCounterHandler _counterHandler;
 | 
					        private readonly IRateLimitCounterHandler _counterHandler;
 | 
				
			||||||
@@ -23,7 +24,8 @@ namespace Ocelot.RateLimit
 | 
				
			|||||||
            return _core.ProcessRequest(requestIdentity, option);
 | 
					            return _core.ProcessRequest(requestIdentity, option);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public string RetryAfterFrom(DateTime timestamp, RateLimitRule rule)
 | 
					
 | 
				
			||||||
 | 
					        public int RetryAfterFrom(DateTime timestamp, RateLimitRule rule)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return _core.RetryAfterFrom(timestamp, rule);
 | 
					            return _core.RetryAfterFrom(timestamp, rule);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -33,5 +35,11 @@ namespace Ocelot.RateLimit
 | 
				
			|||||||
            return _core.GetRateLimitHeaders(context, requestIdentity, option);
 | 
					            return _core.GetRateLimitHeaders(context, requestIdentity, option);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public TimeSpan ConvertToTimeSpan(string timeSpan)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return _core.ConvertToTimeSpan(timeSpan);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -40,7 +40,7 @@ namespace Ocelot.RateLimit.Middleware
 | 
				
			|||||||
                _logger.LogDebug($"EndpointRateLimiting is not enabled for {DownstreamRoute.ReRoute.DownstreamPathTemplate}");
 | 
					                _logger.LogDebug($"EndpointRateLimiting is not enabled for {DownstreamRoute.ReRoute.DownstreamPathTemplate}");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                _logger.TraceInvokeNext();
 | 
					                _logger.TraceInvokeNext();
 | 
				
			||||||
                    await _next.Invoke(context);
 | 
					                await _next.Invoke(context);
 | 
				
			||||||
                _logger.TraceInvokeNextCompleted();
 | 
					                _logger.TraceInvokeNextCompleted();
 | 
				
			||||||
                _logger.TraceMiddlewareCompleted();
 | 
					                _logger.TraceMiddlewareCompleted();
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
@@ -54,7 +54,7 @@ namespace Ocelot.RateLimit.Middleware
 | 
				
			|||||||
                _logger.LogDebug($"{DownstreamRoute.ReRoute.DownstreamPathTemplate} is white listed from rate limiting");
 | 
					                _logger.LogDebug($"{DownstreamRoute.ReRoute.DownstreamPathTemplate} is white listed from rate limiting");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                _logger.TraceInvokeNext();
 | 
					                _logger.TraceInvokeNext();
 | 
				
			||||||
                    await _next.Invoke(context);
 | 
					                await _next.Invoke(context);
 | 
				
			||||||
                _logger.TraceInvokeNextCompleted();
 | 
					                _logger.TraceInvokeNextCompleted();
 | 
				
			||||||
                _logger.TraceMiddlewareCompleted();
 | 
					                _logger.TraceMiddlewareCompleted();
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
@@ -75,21 +75,24 @@ namespace Ocelot.RateLimit.Middleware
 | 
				
			|||||||
                    // log blocked request
 | 
					                    // log blocked request
 | 
				
			||||||
                    LogBlockedRequest(context, identity, counter, rule);
 | 
					                    LogBlockedRequest(context, identity, counter, rule);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    var retrystring = retryAfter.ToString(System.Globalization.CultureInfo.InvariantCulture);
 | 
				
			||||||
                    // break execution
 | 
					                    // break execution
 | 
				
			||||||
                    await ReturnQuotaExceededResponse(context, options, retryAfter);
 | 
					                    await ReturnQuotaExceededResponse(context, options, retrystring);
 | 
				
			||||||
                    _logger.TraceMiddlewareCompleted();
 | 
					                    _logger.TraceMiddlewareCompleted();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            //set X-Rate-Limit headers for the longest period
 | 
					            //set X-Rate-Limit headers for the longest period
 | 
				
			||||||
            if (!options.DisableRateLimitHeaders)
 | 
					            if (!options.DisableRateLimitHeaders)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var headers = _processor.GetRateLimitHeaders( context,identity, options);
 | 
					                var headers = _processor.GetRateLimitHeaders(context, identity, options);
 | 
				
			||||||
                context.Response.OnStarting(SetRateLimitHeaders, state: headers);
 | 
					                context.Response.OnStarting(SetRateLimitHeaders, state: headers);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            _logger.TraceInvokeNext();
 | 
					            _logger.TraceInvokeNext();
 | 
				
			||||||
                await _next.Invoke(context);
 | 
					            await _next.Invoke(context);
 | 
				
			||||||
            _logger.TraceInvokeNextCompleted();
 | 
					            _logger.TraceInvokeNextCompleted();
 | 
				
			||||||
            _logger.TraceMiddlewareCompleted();
 | 
					            _logger.TraceMiddlewareCompleted();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -104,14 +107,18 @@ namespace Ocelot.RateLimit.Middleware
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            return new ClientRequestIdentity(
 | 
					            return new ClientRequestIdentity(
 | 
				
			||||||
                clientId,
 | 
					                clientId,
 | 
				
			||||||
                httpContext.Request.Path.ToString().ToLowerInvariant(), 
 | 
					                httpContext.Request.Path.ToString().ToLowerInvariant(),
 | 
				
			||||||
                httpContext.Request.Method.ToLowerInvariant()
 | 
					                httpContext.Request.Method.ToLowerInvariant()
 | 
				
			||||||
                );
 | 
					                );
 | 
				
			||||||
         }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public bool IsWhitelisted(ClientRequestIdentity requestIdentity, RateLimitOptions option)
 | 
					        public bool IsWhitelisted(ClientRequestIdentity requestIdentity, RateLimitOptions option)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return option.ClientWhitelist.Contains(requestIdentity.ClientId);
 | 
					            if (option.ClientWhitelist.Contains(requestIdentity.ClientId))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                return true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public virtual void LogBlockedRequest(HttpContext httpContext, ClientRequestIdentity identity, RateLimitCounter counter, RateLimitRule rule)
 | 
					        public virtual void LogBlockedRequest(HttpContext httpContext, ClientRequestIdentity identity, RateLimitCounter counter, RateLimitRule rule)
 | 
				
			||||||
@@ -144,6 +151,6 @@ namespace Ocelot.RateLimit.Middleware
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -34,23 +34,45 @@ namespace Ocelot.RateLimit
 | 
				
			|||||||
                if (entry.HasValue)
 | 
					                if (entry.HasValue)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    // entry has not expired
 | 
					                    // entry has not expired
 | 
				
			||||||
                    if (entry.Value.Timestamp + TimeSpan.FromSeconds(rule.PeriodTimespan) >= DateTime.UtcNow)
 | 
					                    if (entry.Value.Timestamp + ConvertToTimeSpan(rule.Period) >= DateTime.UtcNow)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        // increment request count
 | 
					                        // increment request count
 | 
				
			||||||
                        var totalRequests = entry.Value.TotalRequests + 1;
 | 
					                        var totalRequests = entry.Value.TotalRequests + 1;
 | 
				
			||||||
 | 
					 | 
				
			||||||
                        // deep copy
 | 
					                        // deep copy
 | 
				
			||||||
                        counter = new RateLimitCounter(entry.Value.Timestamp, totalRequests);
 | 
					                        counter = new RateLimitCounter(entry.Value.Timestamp, totalRequests);
 | 
				
			||||||
                        
 | 
					 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                // stores: id (string) - timestamp (datetime) - total_requests (long)
 | 
					 | 
				
			||||||
                _counterHandler.Set(counterId, counter, TimeSpan.FromSeconds(rule.PeriodTimespan));
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (counter.TotalRequests > rule.Limit)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                var retryAfter = RetryAfterFrom(counter.Timestamp, rule);
 | 
				
			||||||
 | 
					                if (retryAfter > 0)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    var expirationTime = TimeSpan.FromSeconds(rule.PeriodTimespan);
 | 
				
			||||||
 | 
					                    _counterHandler.Set(counterId, counter, expirationTime);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                else
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    _counterHandler.Remove(counterId);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                var expirationTime = ConvertToTimeSpan(rule.Period);
 | 
				
			||||||
 | 
					                _counterHandler.Set(counterId, counter, expirationTime);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            return counter;
 | 
					            return counter;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void SaveRateLimitCounter(ClientRequestIdentity requestIdentity, RateLimitOptions option, RateLimitCounter counter, TimeSpan expirationTime)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var counterId = ComputeCounterKey(requestIdentity, option);
 | 
				
			||||||
 | 
					            var rule = option.RateLimitRule;
 | 
				
			||||||
 | 
					            // stores: id (string) - timestamp (datetime) - total_requests (long)
 | 
				
			||||||
 | 
					            _counterHandler.Set(counterId, counter, expirationTime);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public RateLimitHeaders GetRateLimitHeaders(HttpContext context, ClientRequestIdentity requestIdentity, RateLimitOptions option)
 | 
					        public RateLimitHeaders GetRateLimitHeaders(HttpContext context, ClientRequestIdentity requestIdentity, RateLimitOptions option)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var rule = option.RateLimitRule;
 | 
					            var rule = option.RateLimitRule;
 | 
				
			||||||
@@ -63,28 +85,28 @@ namespace Ocelot.RateLimit
 | 
				
			|||||||
                    (rule.Limit - entry.Value.TotalRequests).ToString(),
 | 
					                    (rule.Limit - entry.Value.TotalRequests).ToString(),
 | 
				
			||||||
                    (entry.Value.Timestamp + ConvertToTimeSpan(rule.Period)).ToUniversalTime().ToString("o", DateTimeFormatInfo.InvariantInfo)
 | 
					                    (entry.Value.Timestamp + ConvertToTimeSpan(rule.Period)).ToUniversalTime().ToString("o", DateTimeFormatInfo.InvariantInfo)
 | 
				
			||||||
                    );
 | 
					                    );
 | 
				
			||||||
             }
 | 
					            }
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                headers = new RateLimitHeaders(context, 
 | 
					                headers = new RateLimitHeaders(context,
 | 
				
			||||||
                    rule.Period,
 | 
					                    rule.Period,
 | 
				
			||||||
                    rule.Limit.ToString(), 
 | 
					                    rule.Limit.ToString(),
 | 
				
			||||||
                    (DateTime.UtcNow + ConvertToTimeSpan(rule.Period)).ToUniversalTime().ToString("o", DateTimeFormatInfo.InvariantInfo));
 | 
					                    (DateTime.UtcNow + ConvertToTimeSpan(rule.Period)).ToUniversalTime().ToString("o", DateTimeFormatInfo.InvariantInfo));
 | 
				
			||||||
 
 | 
					
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return headers;
 | 
					            return headers;
 | 
				
			||||||
         }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public string ComputeCounterKey(ClientRequestIdentity requestIdentity, RateLimitOptions option)
 | 
					        public string ComputeCounterKey(ClientRequestIdentity requestIdentity, RateLimitOptions option)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var key =  $"{option.RateLimitCounterPrefix}_{requestIdentity.ClientId}_{option.RateLimitRule.Period}_{requestIdentity.HttpVerb}_{requestIdentity.Path}";
 | 
					            var key = $"{option.RateLimitCounterPrefix}_{requestIdentity.ClientId}_{option.RateLimitRule.Period}_{requestIdentity.HttpVerb}_{requestIdentity.Path}";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var idBytes =  Encoding.UTF8.GetBytes(key);
 | 
					            var idBytes = Encoding.UTF8.GetBytes(key);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            byte[] hashBytes;
 | 
					            byte[] hashBytes;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            using (var algorithm =  SHA1.Create())
 | 
					            using (var algorithm = SHA1.Create())
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                hashBytes = algorithm.ComputeHash(idBytes);
 | 
					                hashBytes = algorithm.ComputeHash(idBytes);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@@ -92,12 +114,12 @@ namespace Ocelot.RateLimit
 | 
				
			|||||||
            return BitConverter.ToString(hashBytes).Replace("-", string.Empty);
 | 
					            return BitConverter.ToString(hashBytes).Replace("-", string.Empty);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public string RetryAfterFrom(DateTime timestamp, RateLimitRule rule)
 | 
					        public int RetryAfterFrom(DateTime timestamp, RateLimitRule rule)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var secondsPast = Convert.ToInt32((DateTime.UtcNow - timestamp).TotalSeconds);
 | 
					            var secondsPast = Convert.ToInt32((DateTime.UtcNow - timestamp).TotalSeconds);
 | 
				
			||||||
            var retryAfter = Convert.ToInt32(TimeSpan.FromSeconds(rule.PeriodTimespan).TotalSeconds);
 | 
					            var retryAfter = Convert.ToInt32(TimeSpan.FromSeconds(rule.PeriodTimespan).TotalSeconds);
 | 
				
			||||||
            retryAfter = retryAfter > 1 ? retryAfter - secondsPast : 1;
 | 
					            retryAfter = retryAfter > 1 ? retryAfter - secondsPast : 1;
 | 
				
			||||||
            return retryAfter.ToString(System.Globalization.CultureInfo.InvariantCulture);
 | 
					            return retryAfter;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public TimeSpan ConvertToTimeSpan(string timeSpan)
 | 
					        public TimeSpan ConvertToTimeSpan(string timeSpan)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,7 +22,9 @@ namespace Ocelot.Requester
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        public async Task<Response<HttpResponseMessage>> GetResponse(Request.Request request)
 | 
					        public async Task<Response<HttpResponseMessage>> GetResponse(Request.Request request)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            IHttpClient httpClient = GetHttpClient(request);
 | 
					            var cacheKey = GetCacheKey(request);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            IHttpClient httpClient = GetHttpClient(cacheKey);
 | 
				
			||||||
            try
 | 
					            try
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var response = await httpClient.SendAsync(request.HttpRequestMessage);
 | 
					                var response = await httpClient.SendAsync(request.HttpRequestMessage);
 | 
				
			||||||
@@ -42,32 +44,28 @@ namespace Ocelot.Requester
 | 
				
			|||||||
            {
 | 
					            {
 | 
				
			||||||
                return new ErrorResponse<HttpResponseMessage>(new UnableToCompleteRequestError(exception));
 | 
					                return new ErrorResponse<HttpResponseMessage>(new UnableToCompleteRequestError(exception));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            finally
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                _cacheHandlers.Set(cacheKey, httpClient, TimeSpan.FromHours(24));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private IHttpClient GetHttpClient(Request.Request request)
 | 
					        private IHttpClient GetHttpClient(string cacheKey)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var builder = new HttpClientBuilder();
 | 
					            var builder = new HttpClientBuilder();
 | 
				
			||||||
            var cacheKey = GetCacheKey(request, builder);
 | 
					
 | 
				
			||||||
            var httpClient = _cacheHandlers.Get(cacheKey);
 | 
					            var httpClient = _cacheHandlers.Get(cacheKey);
 | 
				
			||||||
            if (httpClient == null)
 | 
					            if (httpClient == null)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                httpClient = builder.Create();
 | 
					                httpClient = builder.Create();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            _cacheHandlers.Set(cacheKey, httpClient, TimeSpan.FromHours(6));
 | 
					 | 
				
			||||||
            return httpClient;
 | 
					            return httpClient;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private string GetCacheKey(Request.Request request, IHttpClientBuilder builder)
 | 
					        private string GetCacheKey(Request.Request request)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            string baseUrl = $"{request.HttpRequestMessage.RequestUri.Scheme}://{request.HttpRequestMessage.RequestUri.Authority}";
 | 
					            string baseUrl = $"{request.HttpRequestMessage.RequestUri.Scheme}://{request.HttpRequestMessage.RequestUri.Authority}";
 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (request.IsQos)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                builder.WithQos(request.QosProvider, _logger);
 | 
					 | 
				
			||||||
                baseUrl = $"{baseUrl}{request.QosProvider.CircuitBreaker.CircuitBreakerPolicy.PolicyKey}";
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return baseUrl;
 | 
					            return baseUrl;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,6 +6,7 @@ using Ocelot.Values;
 | 
				
			|||||||
using Shouldly;
 | 
					using Shouldly;
 | 
				
			||||||
using TestStack.BDDfy;
 | 
					using TestStack.BDDfy;
 | 
				
			||||||
using Xunit;
 | 
					using Xunit;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Ocelot.UnitTests.LoadBalancer
 | 
					namespace Ocelot.UnitTests.LoadBalancer
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -24,7 +25,7 @@ namespace Ocelot.UnitTests.LoadBalancer
 | 
				
			|||||||
                new Service("product", new HostAndPort("127.0.0.1", 5001), string.Empty, string.Empty, new string[0])
 | 
					                new Service("product", new HostAndPort("127.0.0.1", 5001), string.Empty, string.Empty, new string[0])
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            _roundRobin = new RoundRobinLoadBalancer(_services);
 | 
					            _roundRobin = new RoundRobinLoadBalancer(() => Task.FromResult(_services));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Fact]
 | 
					        [Fact]
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user