13
0
geforkt von Mirrors/Velocity

Refactored rate-limiting.

If rate-limiting is disabled, we now use a simple stub implementation
that is simpler to reason with.
Dieser Commit ist enthalten in:
Andrew Steinborn 2018-11-03 17:36:00 -04:00
Ursprung 89e51bbcb9
Commit a378ccdee0
6 geänderte Dateien mit 68 neuen und 25 gelöschten Zeilen

Datei anzeigen

@ -34,8 +34,9 @@ import com.velocitypowered.proxy.scheduler.VelocityScheduler;
import com.velocitypowered.proxy.server.ServerMap;
import com.velocitypowered.proxy.util.AddressUtil;
import com.velocitypowered.proxy.util.EncryptionUtils;
import com.velocitypowered.proxy.util.Ratelimiter;
import com.velocitypowered.proxy.util.VelocityChannelRegistrar;
import com.velocitypowered.proxy.util.ratelimit.Ratelimiter;
import com.velocitypowered.proxy.util.ratelimit.Ratelimiters;
import io.netty.bootstrap.Bootstrap;
import java.net.InetSocketAddress;
import java.nio.file.Files;
@ -166,7 +167,7 @@ public class VelocityServer implements ProxyServer {
servers.register(new ServerInfo(entry.getKey(), AddressUtil.parseAddress(entry.getValue())));
}
ipAttemptLimiter = new Ratelimiter(configuration.getLoginRatelimit());
ipAttemptLimiter = Ratelimiters.createWithMilliseconds(configuration.getLoginRatelimit());
httpClient = new NettyHttpClient(this);
loadPlugins();

Datei anzeigen

@ -1,6 +1,7 @@
package com.velocitypowered.proxy.util;
package com.velocitypowered.proxy.util.ratelimit;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.base.Ticker;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
@ -11,29 +12,26 @@ import java.util.concurrent.TimeUnit;
/**
* A simple rate-limiter based on a Guava {@link Cache}.
*/
public class Ratelimiter {
public class GuavaCacheRatelimiter implements Ratelimiter {
private final Cache<InetAddress, Long> expiringCache;
private final long timeoutNanos;
public Ratelimiter(long timeoutMs) {
this(timeoutMs, Ticker.systemTicker());
GuavaCacheRatelimiter(long time, TimeUnit unit) {
this(time, unit, Ticker.systemTicker());
}
@VisibleForTesting
Ratelimiter(long timeoutMs, Ticker ticker) {
if (timeoutMs == 0) {
this.timeoutNanos = timeoutMs;
this.expiringCache = CacheBuilder.newBuilder().maximumSize(0).build();
} else {
this.timeoutNanos = TimeUnit.MILLISECONDS.toNanos(timeoutMs);
GuavaCacheRatelimiter(long time, TimeUnit unit, Ticker ticker) {
Preconditions.checkNotNull(unit, "unit");
Preconditions.checkNotNull(ticker, "ticker");
this.timeoutNanos = unit.toNanos(time);
this.expiringCache = CacheBuilder.newBuilder()
.ticker(ticker)
.concurrencyLevel(Runtime.getRuntime().availableProcessors())
.expireAfterWrite(timeoutMs, TimeUnit.MILLISECONDS)
.expireAfterWrite(time, unit)
.build();
}
}
/**
* Attempts to rate-limit the client.
@ -41,10 +39,9 @@ public class Ratelimiter {
* @param address the address to rate limit
* @return true if we should allow the client, false if we should rate-limit
*/
@Override
public boolean attempt(InetAddress address) {
if (timeoutNanos == 0) {
return true;
}
Preconditions.checkNotNull(address, "address");
long expectedNewValue = System.nanoTime() + timeoutNanos;
long last;
try {

Datei anzeigen

@ -0,0 +1,15 @@
package com.velocitypowered.proxy.util.ratelimit;
import java.net.InetAddress;
/**
* A {@link Ratelimiter} that does no rate-limiting.
*/
enum NoopCacheRatelimiter implements Ratelimiter {
INSTANCE;
@Override
public boolean attempt(InetAddress address) {
return true;
}
}

Datei anzeigen

@ -0,0 +1,16 @@
package com.velocitypowered.proxy.util.ratelimit;
import java.net.InetAddress;
/**
* Allows rate limiting of clients.
*/
public interface Ratelimiter {
/**
* Determines whether or not to allow the connection.
* @param address the address to rate limit
* @return true if allowed, false if not
*/
boolean attempt(InetAddress address);
}

Datei anzeigen

@ -0,0 +1,14 @@
package com.velocitypowered.proxy.util.ratelimit;
import java.util.concurrent.TimeUnit;
public final class Ratelimiters {
private Ratelimiters() {
throw new AssertionError();
}
public static Ratelimiter createWithMilliseconds(long ms) {
return ms <= 0 ? NoopCacheRatelimiter.INSTANCE : new GuavaCacheRatelimiter(ms,
TimeUnit.MILLISECONDS);
}
}

Datei anzeigen

@ -1,4 +1,4 @@
package com.velocitypowered.proxy.util;
package com.velocitypowered.proxy.util.ratelimit;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
@ -9,11 +9,11 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import org.junit.jupiter.api.Test;
class RatelimiterTest {
class GuavaCacheRatelimiterTest {
@Test
void attemptZero() {
Ratelimiter noRatelimiter = new Ratelimiter(0);
Ratelimiter noRatelimiter = new GuavaCacheRatelimiter(0, TimeUnit.MILLISECONDS);
assertTrue(noRatelimiter.attempt(InetAddress.getLoopbackAddress()));
assertTrue(noRatelimiter.attempt(InetAddress.getLoopbackAddress()));
}
@ -28,7 +28,7 @@ class RatelimiterTest {
return base + extra.get();
}
};
Ratelimiter ratelimiter = new Ratelimiter(1000, testTicker);
Ratelimiter ratelimiter = new GuavaCacheRatelimiter(1000, TimeUnit.MILLISECONDS, testTicker);
assertTrue(ratelimiter.attempt(InetAddress.getLoopbackAddress()));
assertFalse(ratelimiter.attempt(InetAddress.getLoopbackAddress()));
extra.addAndGet(TimeUnit.SECONDS.toNanos(2));