Mirror von
https://github.com/PaperMC/Velocity.git
synchronisiert 2024-12-25 15:50:19 +01:00
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:
Ursprung
89e51bbcb9
Commit
a378ccdee0
@ -34,8 +34,9 @@ import com.velocitypowered.proxy.scheduler.VelocityScheduler;
|
|||||||
import com.velocitypowered.proxy.server.ServerMap;
|
import com.velocitypowered.proxy.server.ServerMap;
|
||||||
import com.velocitypowered.proxy.util.AddressUtil;
|
import com.velocitypowered.proxy.util.AddressUtil;
|
||||||
import com.velocitypowered.proxy.util.EncryptionUtils;
|
import com.velocitypowered.proxy.util.EncryptionUtils;
|
||||||
import com.velocitypowered.proxy.util.Ratelimiter;
|
|
||||||
import com.velocitypowered.proxy.util.VelocityChannelRegistrar;
|
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 io.netty.bootstrap.Bootstrap;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
@ -166,7 +167,7 @@ public class VelocityServer implements ProxyServer {
|
|||||||
servers.register(new ServerInfo(entry.getKey(), AddressUtil.parseAddress(entry.getValue())));
|
servers.register(new ServerInfo(entry.getKey(), AddressUtil.parseAddress(entry.getValue())));
|
||||||
}
|
}
|
||||||
|
|
||||||
ipAttemptLimiter = new Ratelimiter(configuration.getLoginRatelimit());
|
ipAttemptLimiter = Ratelimiters.createWithMilliseconds(configuration.getLoginRatelimit());
|
||||||
httpClient = new NettyHttpClient(this);
|
httpClient = new NettyHttpClient(this);
|
||||||
loadPlugins();
|
loadPlugins();
|
||||||
|
|
||||||
|
@ -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.annotations.VisibleForTesting;
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.common.base.Ticker;
|
import com.google.common.base.Ticker;
|
||||||
import com.google.common.cache.Cache;
|
import com.google.common.cache.Cache;
|
||||||
import com.google.common.cache.CacheBuilder;
|
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}.
|
* 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 Cache<InetAddress, Long> expiringCache;
|
||||||
private final long timeoutNanos;
|
private final long timeoutNanos;
|
||||||
|
|
||||||
public Ratelimiter(long timeoutMs) {
|
GuavaCacheRatelimiter(long time, TimeUnit unit) {
|
||||||
this(timeoutMs, Ticker.systemTicker());
|
this(time, unit, Ticker.systemTicker());
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
Ratelimiter(long timeoutMs, Ticker ticker) {
|
GuavaCacheRatelimiter(long time, TimeUnit unit, Ticker ticker) {
|
||||||
if (timeoutMs == 0) {
|
Preconditions.checkNotNull(unit, "unit");
|
||||||
this.timeoutNanos = timeoutMs;
|
Preconditions.checkNotNull(ticker, "ticker");
|
||||||
this.expiringCache = CacheBuilder.newBuilder().maximumSize(0).build();
|
this.timeoutNanos = unit.toNanos(time);
|
||||||
} else {
|
|
||||||
this.timeoutNanos = TimeUnit.MILLISECONDS.toNanos(timeoutMs);
|
|
||||||
this.expiringCache = CacheBuilder.newBuilder()
|
this.expiringCache = CacheBuilder.newBuilder()
|
||||||
.ticker(ticker)
|
.ticker(ticker)
|
||||||
.concurrencyLevel(Runtime.getRuntime().availableProcessors())
|
.concurrencyLevel(Runtime.getRuntime().availableProcessors())
|
||||||
.expireAfterWrite(timeoutMs, TimeUnit.MILLISECONDS)
|
.expireAfterWrite(time, unit)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempts to rate-limit the client.
|
* Attempts to rate-limit the client.
|
||||||
@ -41,10 +39,9 @@ public class Ratelimiter {
|
|||||||
* @param address the address to rate limit
|
* @param address the address to rate limit
|
||||||
* @return true if we should allow the client, false if we should rate-limit
|
* @return true if we should allow the client, false if we should rate-limit
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public boolean attempt(InetAddress address) {
|
public boolean attempt(InetAddress address) {
|
||||||
if (timeoutNanos == 0) {
|
Preconditions.checkNotNull(address, "address");
|
||||||
return true;
|
|
||||||
}
|
|
||||||
long expectedNewValue = System.nanoTime() + timeoutNanos;
|
long expectedNewValue = System.nanoTime() + timeoutNanos;
|
||||||
long last;
|
long last;
|
||||||
try {
|
try {
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -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.assertFalse;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
@ -9,11 +9,11 @@ import java.util.concurrent.TimeUnit;
|
|||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
class RatelimiterTest {
|
class GuavaCacheRatelimiterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void attemptZero() {
|
void attemptZero() {
|
||||||
Ratelimiter noRatelimiter = new Ratelimiter(0);
|
Ratelimiter noRatelimiter = new GuavaCacheRatelimiter(0, TimeUnit.MILLISECONDS);
|
||||||
assertTrue(noRatelimiter.attempt(InetAddress.getLoopbackAddress()));
|
assertTrue(noRatelimiter.attempt(InetAddress.getLoopbackAddress()));
|
||||||
assertTrue(noRatelimiter.attempt(InetAddress.getLoopbackAddress()));
|
assertTrue(noRatelimiter.attempt(InetAddress.getLoopbackAddress()));
|
||||||
}
|
}
|
||||||
@ -28,7 +28,7 @@ class RatelimiterTest {
|
|||||||
return base + extra.get();
|
return base + extra.get();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ratelimiter ratelimiter = new Ratelimiter(1000, testTicker);
|
Ratelimiter ratelimiter = new GuavaCacheRatelimiter(1000, TimeUnit.MILLISECONDS, testTicker);
|
||||||
assertTrue(ratelimiter.attempt(InetAddress.getLoopbackAddress()));
|
assertTrue(ratelimiter.attempt(InetAddress.getLoopbackAddress()));
|
||||||
assertFalse(ratelimiter.attempt(InetAddress.getLoopbackAddress()));
|
assertFalse(ratelimiter.attempt(InetAddress.getLoopbackAddress()));
|
||||||
extra.addAndGet(TimeUnit.SECONDS.toNanos(2));
|
extra.addAndGet(TimeUnit.SECONDS.toNanos(2));
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren