From 2821def4fdfdf2fa405311fbb1030f7300e0e6ee Mon Sep 17 00:00:00 2001 From: Spencer Gibb Date: Wed, 8 Apr 2020 22:49:04 -0400 Subject: [PATCH] Allows 0 burstCapacity. Fixes gh-1312 --- .../filter/ratelimit/RedisRateLimiter.java | 34 +++++++++++-------- .../META-INF/scripts/request_rate_limiter.lua | 7 ++-- .../ratelimit/RedisRateLimiterTests.java | 18 ++++++++++ 3 files changed, 42 insertions(+), 17 deletions(-) diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/ratelimit/RedisRateLimiter.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/ratelimit/RedisRateLimiter.java index 85b4a623..2ab47e11 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/ratelimit/RedisRateLimiter.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/ratelimit/RedisRateLimiter.java @@ -282,22 +282,26 @@ public class RedisRateLimiter extends AbstractRateLimiter> flux = this.redisTemplate.execute(this.script, keys, scriptArgs); // .log("redisratelimiter", Level.FINER); - return flux.onErrorResume(throwable -> Flux.just(Arrays.asList(1L, -1L))) - .reduce(new ArrayList(), (longs, l) -> { - longs.addAll(l); - return longs; - }).map(results -> { - boolean allowed = results.get(0) == 1L; - Long tokensLeft = results.get(1); + return flux.onErrorResume(throwable -> { + if (log.isDebugEnabled()) { + log.debug("Error calling rate limiter lua", throwable); + } + return Flux.just(Arrays.asList(1L, -1L)); + }).reduce(new ArrayList(), (longs, l) -> { + longs.addAll(l); + return longs; + }).map(results -> { + boolean allowed = results.get(0) == 1L; + Long tokensLeft = results.get(1); - Response response = new Response(allowed, - getHeaders(routeConfig, tokensLeft)); + Response response = new Response(allowed, + getHeaders(routeConfig, tokensLeft)); - if (log.isDebugEnabled()) { - log.debug("response: " + response); - } - return response; - }); + if (log.isDebugEnabled()) { + log.debug("response: " + response); + } + return response; + }); } catch (Exception e) { /* @@ -345,7 +349,7 @@ public class RedisRateLimiter extends AbstractRateLimiter 0 then + redis.call("setex", tokens_key, ttl, new_tokens) + redis.call("setex", timestamp_key, ttl, now) +end +-- return { allowed_num, new_tokens, capacity, filled_tokens, requested, new_tokens } return { allowed_num, new_tokens } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/ratelimit/RedisRateLimiterTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/ratelimit/RedisRateLimiterTests.java index 7636a858..9c2eb087 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/ratelimit/RedisRateLimiterTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/ratelimit/RedisRateLimiterTests.java @@ -104,6 +104,24 @@ public class RedisRateLimiterTests extends BaseWebClientTests { checkLimitEnforced(id, replenishRate, burstCapacity, requestedTokens, routeId); } + @Test + public void redisRateLimiterWorksForZeroBurstCapacity() throws Exception { + String id = UUID.randomUUID().toString(); + + int replenishRate = 1; + int burstCapacity = 0; + int requestedTokens = 1; + + String routeId = "zero_burst_capacity_route"; + rateLimiter.getConfig().put(routeId, + new RedisRateLimiter.Config().setBurstCapacity(burstCapacity) + .setReplenishRate(replenishRate) + .setRequestedTokens(requestedTokens)); + + Response response = rateLimiter.isAllowed(routeId, id).block(); + assertThat(response.isAllowed()).isFalse(); + } + @Test public void keysUseRedisKeyHashTags() { assertThat(RedisRateLimiter.getKeys("1")).containsExactly(