Skip to content

Commit 96775ee

Browse files
committed
Merge branch 'TheFreaky/main'
2 parents 48a95fb + 086a586 commit 96775ee

File tree

3 files changed

+40
-6
lines changed

3 files changed

+40
-6
lines changed

spring-cloud-gateway-server-webflux/src/main/java/org/springframework/cloud/gateway/filter/ratelimit/RedisRateLimiter.java

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,13 @@ public class RedisRateLimiter extends AbstractRateLimiter<RedisRateLimiter.Confi
8484
*/
8585
public static final String REQUESTED_TOKENS_HEADER = "X-RateLimit-Requested-Tokens";
8686

87+
/**
88+
* Constant representing the maximum number that can be safely used in Redis scripts.
89+
* This is the largest number that can be represented accurately in Java's `long` type
90+
* and is equal to 2^53 - 1, which is the largest number that Lua can reliably handle.
91+
*/
92+
private static final Long REDIS_LUA_MAX_SAFE_INTEGER = 9007199254740991L;
93+
8794
private Log log = LogFactory.getLog(getClass());
8895

8996
private ReactiveStringRedisTemplate redisTemplate;
@@ -130,7 +137,7 @@ public RedisRateLimiter(ReactiveStringRedisTemplate redisTemplate, RedisScript<L
130137
* @param defaultBurstCapacity how many tokens the bucket can hold in token-bucket
131138
* algorithm.
132139
*/
133-
public RedisRateLimiter(int defaultReplenishRate, int defaultBurstCapacity) {
140+
public RedisRateLimiter(int defaultReplenishRate, long defaultBurstCapacity) {
134141
super(Config.class, CONFIGURATION_PROPERTY_NAME, (ConfigurationService) null);
135142
this.defaultConfig = new Config().setReplenishRate(defaultReplenishRate).setBurstCapacity(defaultBurstCapacity);
136143
}
@@ -142,7 +149,7 @@ public RedisRateLimiter(int defaultReplenishRate, int defaultBurstCapacity) {
142149
* algorithm.
143150
* @param defaultRequestedTokens how many tokens are requested per request.
144151
*/
145-
public RedisRateLimiter(int defaultReplenishRate, int defaultBurstCapacity, int defaultRequestedTokens) {
152+
public RedisRateLimiter(int defaultReplenishRate, long defaultBurstCapacity, int defaultRequestedTokens) {
146153
this(defaultReplenishRate, defaultBurstCapacity);
147154
this.defaultConfig.setRequestedTokens(defaultRequestedTokens);
148155
}
@@ -241,7 +248,7 @@ public Mono<Response> isAllowed(String routeId, String id) {
241248
int replenishRate = routeConfig.getReplenishRate();
242249

243250
// How much bursting do you want to allow?
244-
int burstCapacity = routeConfig.getBurstCapacity();
251+
long burstCapacity = routeConfig.getBurstCapacity();
245252

246253
// How many tokens are requested per request?
247254
int requestedTokens = routeConfig.getRequestedTokens();
@@ -314,7 +321,7 @@ public static class Config {
314321
private int replenishRate;
315322

316323
@Min(0)
317-
private int burstCapacity = 1;
324+
private long burstCapacity = 1;
318325

319326
@Min(1)
320327
private int requestedTokens = 1;
@@ -328,13 +335,15 @@ public Config setReplenishRate(int replenishRate) {
328335
return this;
329336
}
330337

331-
public int getBurstCapacity() {
338+
public long getBurstCapacity() {
332339
return burstCapacity;
333340
}
334341

335-
public Config setBurstCapacity(int burstCapacity) {
342+
public Config setBurstCapacity(long burstCapacity) {
336343
Assert.isTrue(burstCapacity >= this.replenishRate, "BurstCapacity(" + burstCapacity
337344
+ ") must be greater than or equal than replenishRate(" + this.replenishRate + ")");
345+
Assert.isTrue(burstCapacity <= REDIS_LUA_MAX_SAFE_INTEGER, "BurstCapacity(" + burstCapacity
346+
+ ") must not exceed the maximum allowed value of " + REDIS_LUA_MAX_SAFE_INTEGER);
338347
this.burstCapacity = burstCapacity;
339348
return this;
340349
}

spring-cloud-gateway-server-webflux/src/test/java/org/springframework/cloud/gateway/filter/ratelimit/RedisRateLimiterConfigTests.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,14 @@ public void shouldThrowAnErrorWhenReplenishRateIsHigherThanBurstCapacity() {
5858
Assertions.assertThatThrownBy(() -> new RedisRateLimiter(10, 5)).isInstanceOf(IllegalArgumentException.class);
5959
}
6060

61+
@Test
62+
public void shouldThrowAnErrorWhenBurstCapacityExceedsMaxAllowed() {
63+
long burstCapacity = Long.MAX_VALUE;
64+
65+
Assertions.assertThatThrownBy(() -> new RedisRateLimiter(10, burstCapacity))
66+
.isInstanceOf(IllegalArgumentException.class);
67+
}
68+
6169
@Test
6270
public void redisRateConfiguredFromEnvironment() {
6371
assertFilter("redis_rate_limiter_config_test", 10, 20, 1, false);

spring-cloud-gateway-server-webflux/src/test/java/org/springframework/cloud/gateway/filter/ratelimit/RedisRateLimiterLuaScriptTests.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
public class RedisRateLimiterLuaScriptTests {
4848

4949
static final String KEY_PREFIX = "redis-rate-limiter-lua-script-tests";
50+
static final Long REDIS_LUA_MAX_SAFE_INTEGER = 9007199254740991L;
5051

5152
@Container
5253
public static GenericContainer redis = new GenericContainer<>("redis:5.0.14-alpine").withExposedPorts(6379);
@@ -147,6 +148,22 @@ public void testTokensNotEnough() {
147148
assertThat(result.get(1)).isEqualTo(10);
148149
}
149150

151+
@Test
152+
public void testCapacityExceedsMaxInt() {
153+
long rate = 1;
154+
long capacity = REDIS_LUA_MAX_SAFE_INTEGER;
155+
long now = System.currentTimeMillis();
156+
long requested = 1;
157+
158+
List<String> keys = getKeys("capacity_exceeds_max_int");
159+
List<String> args = getArgs(rate, capacity, now, requested);
160+
161+
List<Long> result = redisTemplate.execute(redisScript, keys, args).blockFirst();
162+
163+
assertThat(result.get(0)).isEqualTo(1);
164+
assertThat(result.get(1)).isEqualTo(REDIS_LUA_MAX_SAFE_INTEGER - 1);
165+
}
166+
150167
@EnableAutoConfiguration
151168
@SpringBootConfiguration
152169
public static class TestConfig {

0 commit comments

Comments
 (0)