mirror of
				https://github.com/nsnail/Ocelot.git
				synced 2025-11-04 15:30:49 +08:00 
			
		
		
		
	refactor code
This commit is contained in:
		@@ -117,12 +117,8 @@ namespace Ocelot.Configuration.Creator
 | 
			
		||||
                rateLimitOption = new RateLimitOptions(enableRateLimiting, globalConfiguration.RateLimitOptions.ClientIdHeader, 
 | 
			
		||||
                   fileReRoute.RateLimitOptions.ClientWhitelist, globalConfiguration.RateLimitOptions.DisableRateLimitHeaders,
 | 
			
		||||
                   globalConfiguration.RateLimitOptions.QuotaExceededMessage, globalConfiguration.RateLimitOptions.RateLimitCounterPrefix,
 | 
			
		||||
                   new RateLimitRule()
 | 
			
		||||
                   {
 | 
			
		||||
                       Limit = fileReRoute.RateLimitOptions.Limit,
 | 
			
		||||
                       Period = fileReRoute.RateLimitOptions.Period,
 | 
			
		||||
                       PeriodTimespan = TimeSpan.FromSeconds(fileReRoute.RateLimitOptions.PeriodTimespan)
 | 
			
		||||
                   }, globalConfiguration.RateLimitOptions.HttpStatusCode);                
 | 
			
		||||
                   new RateLimitRule(fileReRoute.RateLimitOptions.Period, TimeSpan.FromSeconds(fileReRoute.RateLimitOptions.PeriodTimespan), fileReRoute.RateLimitOptions.Limit)
 | 
			
		||||
                   , globalConfiguration.RateLimitOptions.HttpStatusCode);                
 | 
			
		||||
            }
 | 
			
		||||
            var serviceProviderPort = globalConfiguration?.ServiceDiscoveryProvider?.Port ?? 0;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -32,7 +32,7 @@ namespace Ocelot.Configuration.File
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets or sets the HTTP Status code returned when rate limiting occurs, by default value is set to 429 (Too Many Requests)
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public int HttpStatusCode { get; private set; } = 429;
 | 
			
		||||
        public int HttpStatusCode { get;  set; } = 429;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 
 | 
			
		||||
@@ -25,7 +25,7 @@ namespace Ocelot.Configuration.File
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public string Period { get; set; }
 | 
			
		||||
 | 
			
		||||
        public int PeriodTimespan { get; set; }
 | 
			
		||||
        public double PeriodTimespan { get; set; }
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Maximum number of requests that a client can make in a defined period
 | 
			
		||||
        /// </summary>
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,7 @@ namespace Ocelot.Configuration
 | 
			
		||||
        {
 | 
			
		||||
            EnableRateLimiting = enbleRateLimiting;
 | 
			
		||||
            ClientIdHeader = clientIdHeader;
 | 
			
		||||
            ClientWhitelist = clientWhitelist;
 | 
			
		||||
            ClientWhitelist = clientWhitelist?? new List<string>();
 | 
			
		||||
            DisableRateLimitHeaders = disableRateLimitHeaders;
 | 
			
		||||
            QuotaExceededMessage = quotaExceededMessage;
 | 
			
		||||
            RateLimitCounterPrefix = rateLimitCounterPrefix;
 | 
			
		||||
@@ -62,15 +62,22 @@ namespace Ocelot.Configuration
 | 
			
		||||
 | 
			
		||||
    public class RateLimitRule
 | 
			
		||||
    {
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Rate limit period as in 1s, 1m, 1h
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public string Period { get; set; }
 | 
			
		||||
        public RateLimitRule(string period, TimeSpan periodTimespan, long limit)
 | 
			
		||||
        {
 | 
			
		||||
            Period = period;
 | 
			
		||||
            PeriodTimespan = periodTimespan;
 | 
			
		||||
            Limit = limit;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public TimeSpan? PeriodTimespan { get; set; }
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Rate limit period as in 1s, 1m, 1h,1d
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public string Period { get; private set; }
 | 
			
		||||
 | 
			
		||||
        public TimeSpan PeriodTimespan { get; private set; }
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Maximum number of requests that a client can make in a defined period
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public long Limit { get; set; }
 | 
			
		||||
        public long Limit { get; private set; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -91,7 +91,7 @@ namespace Ocelot.DependencyInjection
 | 
			
		||||
            // could maybe use a scoped data repository
 | 
			
		||||
            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
 | 
			
		||||
            services.AddScoped<IRequestScopedDataRepository, HttpDataRepository>();
 | 
			
		||||
 | 
			
		||||
            services.AddMemoryCache();
 | 
			
		||||
            return services;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
using Ocelot.Configuration;
 | 
			
		||||
using Microsoft.AspNetCore.Http;
 | 
			
		||||
using Ocelot.Configuration;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
@@ -27,9 +28,9 @@ namespace Ocelot.RateLimit
 | 
			
		||||
            return _core.RetryAfterFrom(timestamp, rule);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public RateLimitHeaders GetRateLimitHeaders(ClientRequestIdentity requestIdentity, RateLimitOptions option)
 | 
			
		||||
        public RateLimitHeaders GetRateLimitHeaders(HttpContext context, ClientRequestIdentity requestIdentity, RateLimitOptions option)
 | 
			
		||||
        {
 | 
			
		||||
            return _core.GetRateLimitHeaders(requestIdentity, option);
 | 
			
		||||
            return _core.GetRateLimitHeaders(context, requestIdentity, option);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -2,10 +2,17 @@
 | 
			
		||||
{
 | 
			
		||||
    public class ClientRequestIdentity
 | 
			
		||||
    {
 | 
			
		||||
        public string ClientId { get; set; }
 | 
			
		||||
        public ClientRequestIdentity(string clientId, string path, string httpverb)
 | 
			
		||||
        {
 | 
			
		||||
            ClientId = clientId;
 | 
			
		||||
            Path = path;
 | 
			
		||||
            HttpVerb = httpverb;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public string Path { get; set; }
 | 
			
		||||
        public string ClientId { get; private set; }
 | 
			
		||||
 | 
			
		||||
        public string HttpVerb { get; set; }
 | 
			
		||||
        public string Path { get; private set; }
 | 
			
		||||
 | 
			
		||||
        public string HttpVerb { get; private set; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -72,9 +72,7 @@ namespace Ocelot.RateLimit.Middleware
 | 
			
		||||
            //set X-Rate-Limit headers for the longest period
 | 
			
		||||
            if (!options.DisableRateLimitHeaders)
 | 
			
		||||
            {
 | 
			
		||||
                var headers = _processor.GetRateLimitHeaders(identity, options);
 | 
			
		||||
                headers.Context = context;
 | 
			
		||||
 | 
			
		||||
                var headers = _processor.GetRateLimitHeaders( context,identity, options);
 | 
			
		||||
                context.Response.OnStarting(SetRateLimitHeaders, state: headers);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@@ -89,21 +87,19 @@ namespace Ocelot.RateLimit.Middleware
 | 
			
		||||
                clientId = httpContext.Request.Headers[option.ClientIdHeader].First();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return new ClientRequestIdentity
 | 
			
		||||
            {
 | 
			
		||||
                Path = httpContext.Request.Path.ToString().ToLowerInvariant(),
 | 
			
		||||
                HttpVerb = httpContext.Request.Method.ToLowerInvariant(),
 | 
			
		||||
                ClientId = clientId,
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
            return new ClientRequestIdentity(
 | 
			
		||||
                clientId,
 | 
			
		||||
                httpContext.Request.Path.ToString().ToLowerInvariant(), 
 | 
			
		||||
                httpContext.Request.Method.ToLowerInvariant()
 | 
			
		||||
                );
 | 
			
		||||
         }
 | 
			
		||||
 | 
			
		||||
        public bool IsWhitelisted(ClientRequestIdentity requestIdentity, RateLimitOptions option)
 | 
			
		||||
        {
 | 
			
		||||
            if (option.ClientWhitelist != null && option.ClientWhitelist.Contains(requestIdentity.ClientId))
 | 
			
		||||
            if (option.ClientWhitelist.Contains(requestIdentity.ClientId))
 | 
			
		||||
            {
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,11 @@
 | 
			
		||||
using Ocelot.Configuration;
 | 
			
		||||
using Microsoft.AspNetCore.Http;
 | 
			
		||||
using Ocelot.Configuration;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Globalization;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Security.Cryptography;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.RateLimit
 | 
			
		||||
@@ -19,11 +22,7 @@ namespace Ocelot.RateLimit
 | 
			
		||||
 | 
			
		||||
        public RateLimitCounter ProcessRequest(ClientRequestIdentity requestIdentity, RateLimitOptions option)
 | 
			
		||||
        {
 | 
			
		||||
            var counter = new RateLimitCounter
 | 
			
		||||
            {
 | 
			
		||||
                Timestamp = DateTime.UtcNow,
 | 
			
		||||
                TotalRequests = 1
 | 
			
		||||
            };
 | 
			
		||||
            RateLimitCounter counter = new RateLimitCounter(DateTime.UtcNow, 1);
 | 
			
		||||
            var rule = option.RateLimitRule;
 | 
			
		||||
 | 
			
		||||
            var counterId = ComputeCounterKey(requestIdentity, option);
 | 
			
		||||
@@ -35,59 +34,57 @@ namespace Ocelot.RateLimit
 | 
			
		||||
                if (entry.HasValue)
 | 
			
		||||
                {
 | 
			
		||||
                    // entry has not expired
 | 
			
		||||
                    if (entry.Value.Timestamp + rule.PeriodTimespan.Value >= DateTime.UtcNow)
 | 
			
		||||
                    if (entry.Value.Timestamp + rule.PeriodTimespan >= DateTime.UtcNow)
 | 
			
		||||
                    {
 | 
			
		||||
                        // increment request count
 | 
			
		||||
                        var totalRequests = entry.Value.TotalRequests + 1;
 | 
			
		||||
 | 
			
		||||
                        // deep copy
 | 
			
		||||
                        counter = new RateLimitCounter
 | 
			
		||||
                        {
 | 
			
		||||
                            Timestamp = entry.Value.Timestamp,
 | 
			
		||||
                            TotalRequests = totalRequests
 | 
			
		||||
                        };
 | 
			
		||||
                        counter = new RateLimitCounter(entry.Value.Timestamp, totalRequests);
 | 
			
		||||
                        
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // stores: id (string) - timestamp (datetime) - total_requests (long)
 | 
			
		||||
                _counterHandler.Set(counterId, counter, rule.PeriodTimespan.Value);
 | 
			
		||||
                _counterHandler.Set(counterId, counter, rule.PeriodTimespan);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return counter;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public RateLimitHeaders GetRateLimitHeaders(ClientRequestIdentity requestIdentity, RateLimitOptions option)
 | 
			
		||||
        public RateLimitHeaders GetRateLimitHeaders(HttpContext context, ClientRequestIdentity requestIdentity, RateLimitOptions option)
 | 
			
		||||
        {
 | 
			
		||||
            var rule = option.RateLimitRule;
 | 
			
		||||
            var headers = new RateLimitHeaders();
 | 
			
		||||
            RateLimitHeaders headers = null;
 | 
			
		||||
            var counterId = ComputeCounterKey(requestIdentity, option);
 | 
			
		||||
            var entry = _counterHandler.Get(counterId);
 | 
			
		||||
            if (entry.HasValue)
 | 
			
		||||
            {
 | 
			
		||||
                headers.Reset = (entry.Value.Timestamp + ConvertToTimeSpan(rule.Period)).ToUniversalTime().ToString("o", DateTimeFormatInfo.InvariantInfo);
 | 
			
		||||
                headers.Limit = rule.Period;
 | 
			
		||||
                headers.Remaining = (rule.Limit - entry.Value.TotalRequests).ToString();
 | 
			
		||||
            }
 | 
			
		||||
                headers = new RateLimitHeaders(context, rule.Period,
 | 
			
		||||
                    (rule.Limit - entry.Value.TotalRequests).ToString(),
 | 
			
		||||
                    (entry.Value.Timestamp + ConvertToTimeSpan(rule.Period)).ToUniversalTime().ToString("o", DateTimeFormatInfo.InvariantInfo)
 | 
			
		||||
                    );
 | 
			
		||||
             }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                headers.Reset = (DateTime.UtcNow + ConvertToTimeSpan(rule.Period)).ToUniversalTime().ToString("o", DateTimeFormatInfo.InvariantInfo);
 | 
			
		||||
                headers.Limit = rule.Period;
 | 
			
		||||
                headers.Remaining = rule.Limit.ToString();
 | 
			
		||||
                headers = new RateLimitHeaders(context, 
 | 
			
		||||
                    rule.Period,
 | 
			
		||||
                    rule.Limit.ToString(), 
 | 
			
		||||
                    (DateTime.UtcNow + ConvertToTimeSpan(rule.Period)).ToUniversalTime().ToString("o", DateTimeFormatInfo.InvariantInfo));
 | 
			
		||||
 
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return headers;
 | 
			
		||||
            throw new NotImplementedException();
 | 
			
		||||
        }
 | 
			
		||||
         }
 | 
			
		||||
 | 
			
		||||
        public string ComputeCounterKey(ClientRequestIdentity requestIdentity, RateLimitOptions option)
 | 
			
		||||
        {
 | 
			
		||||
            var key =  $"{option.RateLimitCounterPrefix}_{requestIdentity.ClientId}_{option.RateLimitRule.Period}_{requestIdentity.HttpVerb}_{requestIdentity.Path}";
 | 
			
		||||
 | 
			
		||||
            var idBytes = System.Text.Encoding.UTF8.GetBytes(key);
 | 
			
		||||
            var idBytes =  Encoding.UTF8.GetBytes(key);
 | 
			
		||||
 | 
			
		||||
            byte[] hashBytes;
 | 
			
		||||
 | 
			
		||||
            using (var algorithm = System.Security.Cryptography.SHA1.Create())
 | 
			
		||||
            using (var algorithm =  SHA1.Create())
 | 
			
		||||
            {
 | 
			
		||||
                hashBytes = algorithm.ComputeHash(idBytes);
 | 
			
		||||
            }
 | 
			
		||||
@@ -98,7 +95,7 @@ namespace Ocelot.RateLimit
 | 
			
		||||
        public string RetryAfterFrom(DateTime timestamp, RateLimitRule rule)
 | 
			
		||||
        {
 | 
			
		||||
            var secondsPast = Convert.ToInt32((DateTime.UtcNow - timestamp).TotalSeconds);
 | 
			
		||||
            var retryAfter = Convert.ToInt32(rule.PeriodTimespan.Value.TotalSeconds);
 | 
			
		||||
            var retryAfter = Convert.ToInt32(rule.PeriodTimespan.TotalSeconds);
 | 
			
		||||
            retryAfter = retryAfter > 1 ? retryAfter - secondsPast : 1;
 | 
			
		||||
            return retryAfter.ToString(System.Globalization.CultureInfo.InvariantCulture);
 | 
			
		||||
        }
 | 
			
		||||
@@ -111,11 +108,16 @@ namespace Ocelot.RateLimit
 | 
			
		||||
 | 
			
		||||
            switch (type)
 | 
			
		||||
            {
 | 
			
		||||
                case "d": return TimeSpan.FromDays(double.Parse(value));
 | 
			
		||||
                case "h": return TimeSpan.FromHours(double.Parse(value));
 | 
			
		||||
                case "m": return TimeSpan.FromMinutes(double.Parse(value));
 | 
			
		||||
                case "s": return TimeSpan.FromSeconds(double.Parse(value));
 | 
			
		||||
                default: throw new FormatException($"{timeSpan} can't be converted to TimeSpan, unknown type {type}");
 | 
			
		||||
                case "d":
 | 
			
		||||
                    return TimeSpan.FromDays(double.Parse(value));
 | 
			
		||||
                case "h":
 | 
			
		||||
                    return TimeSpan.FromHours(double.Parse(value));
 | 
			
		||||
                case "m":
 | 
			
		||||
                    return TimeSpan.FromMinutes(double.Parse(value));
 | 
			
		||||
                case "s":
 | 
			
		||||
                    return TimeSpan.FromSeconds(double.Parse(value));
 | 
			
		||||
                default:
 | 
			
		||||
                    throw new FormatException($"{timeSpan} can't be converted to TimeSpan, unknown type {type}");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -10,8 +10,14 @@ namespace Ocelot.RateLimit
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public struct RateLimitCounter
 | 
			
		||||
    {
 | 
			
		||||
        public DateTime Timestamp { get; set; }
 | 
			
		||||
        public RateLimitCounter(DateTime timestamp, long totalRequest)
 | 
			
		||||
        {
 | 
			
		||||
            Timestamp = timestamp;
 | 
			
		||||
            TotalRequests = totalRequest;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public long TotalRequests { get; set; }
 | 
			
		||||
        public DateTime Timestamp { get; private set; }
 | 
			
		||||
 | 
			
		||||
        public long TotalRequests { get; private set; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -8,12 +8,20 @@ namespace Ocelot.RateLimit
 | 
			
		||||
{
 | 
			
		||||
    public class RateLimitHeaders
 | 
			
		||||
    {
 | 
			
		||||
        public HttpContext Context { get; set; }
 | 
			
		||||
        public RateLimitHeaders(HttpContext context, string limit, string remaining, string reset)
 | 
			
		||||
        {
 | 
			
		||||
            Context = context;
 | 
			
		||||
            Limit = limit;
 | 
			
		||||
            Remaining = remaining;
 | 
			
		||||
            Reset = reset;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public string Limit { get; set; }
 | 
			
		||||
        public HttpContext Context { get; private set; }
 | 
			
		||||
 | 
			
		||||
        public string Remaining { get; set; }
 | 
			
		||||
        public string Limit { get; private set; }
 | 
			
		||||
 | 
			
		||||
        public string Reset { get; set; }
 | 
			
		||||
        public string Remaining { get; private set; }
 | 
			
		||||
 | 
			
		||||
        public string Reset { get; private set; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user