1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2024-12-14 04:00:30 +00:00

Add config options for a global bandwidth limit

This uses Netty's global traffic shaping handlers to limit the rate at
which packets can be sent and received. If the bandwidth limit is hit,
we'll start dropping packets, which will mean remote servers send
traffic to us at a much slower pace.

This isn't perfect, as there is only a global limit, and not a
per-computer one. As a result, its possible for one computer to use
all/most bandwidth, and thus slow down other computers.

This would be something to improve on in the future. However, I've spent
a lot of time reading the netty source code and docs, and the
implementation for that is significantly more complex, and one I'm not
comfortable working on right now.

For the time being, this satisfies the issues in #33 and hopefully
alleviates server owner's concerns about the http API. Remaining
problems can either be solved by moderation (with help of the
//computercraft track` command) or future updates.

Closes #33
This commit is contained in:
Jonathan Coates 2021-07-28 15:53:22 +01:00
parent 227b444d81
commit f74c4cc83c
No known key found for this signature in database
GPG Key ID: B9E431FF07C98D06
6 changed files with 51 additions and 7 deletions

View File

@ -52,6 +52,8 @@ public final class ComputerCraft
public static int httpMaxRequests = 16; public static int httpMaxRequests = 16;
public static int httpMaxWebsockets = 4; public static int httpMaxWebsockets = 4;
public static int httpDownloadBandwidth = 32 * 1024 * 1024;
public static int httpUploadBandwidth = 32 * 1024 * 1024;
public static boolean enableCommandBlock = false; public static boolean enableCommandBlock = false;
public static int modemRange = 64; public static int modemRange = 64;

View File

@ -20,6 +20,8 @@ import io.netty.handler.codec.http.websocketx.WebSocketHandshakeException;
import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder; import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.timeout.ReadTimeoutException; import io.netty.handler.timeout.ReadTimeoutException;
import io.netty.handler.traffic.AbstractTrafficShapingHandler;
import io.netty.handler.traffic.GlobalTrafficShapingHandler;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.net.ssl.SSLException; import javax.net.ssl.SSLException;
@ -28,9 +30,7 @@ import javax.net.ssl.TrustManagerFactory;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.URI; import java.net.URI;
import java.security.KeyStore; import java.security.KeyStore;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
/** /**
@ -38,10 +38,8 @@ import java.util.concurrent.TimeUnit;
*/ */
public final class NetworkUtils public final class NetworkUtils
{ {
public static final ExecutorService EXECUTOR = new ThreadPoolExecutor( public static final ScheduledThreadPoolExecutor EXECUTOR = new ScheduledThreadPoolExecutor(
4, Integer.MAX_VALUE, 4,
60L, TimeUnit.SECONDS,
new SynchronousQueue<>(),
ThreadUtils.builder( "Network" ) ThreadUtils.builder( "Network" )
.setPriority( Thread.MIN_PRIORITY + (Thread.NORM_PRIORITY - Thread.MIN_PRIORITY) / 2 ) .setPriority( Thread.MIN_PRIORITY + (Thread.NORM_PRIORITY - Thread.MIN_PRIORITY) / 2 )
.build() .build()
@ -52,6 +50,15 @@ public final class NetworkUtils
.build() .build()
); );
public static final AbstractTrafficShapingHandler SHAPING_HANDLER = new GlobalTrafficShapingHandler(
EXECUTOR, ComputerCraft.httpUploadBandwidth, ComputerCraft.httpDownloadBandwidth
);
static
{
EXECUTOR.setKeepAliveTime( 60, TimeUnit.SECONDS );
}
private NetworkUtils() private NetworkUtils()
{ {
} }
@ -107,6 +114,16 @@ public final class NetworkUtils
} }
} }
public static void reloadConfig()
{
SHAPING_HANDLER.configure( ComputerCraft.httpUploadBandwidth, ComputerCraft.httpDownloadBandwidth );
}
public static void reset()
{
SHAPING_HANDLER.trafficCounter().resetCumulativeTime();
}
/** /**
* Create a {@link InetSocketAddress} from a {@link java.net.URI}. * Create a {@link InetSocketAddress} from a {@link java.net.URI}.
* *

View File

@ -167,6 +167,7 @@ public class HttpRequest extends Resource<HttpRequest>
} }
ChannelPipeline p = ch.pipeline(); ChannelPipeline p = ch.pipeline();
p.addLast( NetworkUtils.SHAPING_HANDLER );
if( sslContext != null ) if( sslContext != null )
{ {
p.addLast( sslContext.newHandler( ch.alloc(), uri.getHost(), socketAddress.getPort() ) ); p.addLast( sslContext.newHandler( ch.alloc(), uri.getHost(), socketAddress.getPort() ) );

View File

@ -145,6 +145,7 @@ public class Websocket extends Resource<Websocket>
protected void initChannel( SocketChannel ch ) protected void initChannel( SocketChannel ch )
{ {
ChannelPipeline p = ch.pipeline(); ChannelPipeline p = ch.pipeline();
p.addLast( NetworkUtils.SHAPING_HANDLER );
if( sslContext != null ) if( sslContext != null )
{ {
p.addLast( sslContext.newHandler( ch.alloc(), uri.getHost(), socketAddress.getPort() ) ); p.addLast( sslContext.newHandler( ch.alloc(), uri.getHost(), socketAddress.getPort() ) );

View File

@ -6,6 +6,7 @@
package dan200.computercraft.shared; package dan200.computercraft.shared;
import dan200.computercraft.ComputerCraft; import dan200.computercraft.ComputerCraft;
import dan200.computercraft.core.apis.http.NetworkUtils;
import dan200.computercraft.core.computer.MainThread; import dan200.computercraft.core.computer.MainThread;
import dan200.computercraft.core.tracking.Tracking; import dan200.computercraft.core.tracking.Tracking;
import dan200.computercraft.shared.command.CommandComputerCraft; import dan200.computercraft.shared.command.CommandComputerCraft;
@ -83,6 +84,7 @@ public final class CommonHooks
ComputerCraft.serverComputerRegistry.reset(); ComputerCraft.serverComputerRegistry.reset();
WirelessNetwork.resetNetworks(); WirelessNetwork.resetNetworks();
Tracking.reset(); Tracking.reset();
NetworkUtils.reset();
} }
@SubscribeEvent @SubscribeEvent
@ -91,6 +93,7 @@ public final class CommonHooks
ComputerCraft.serverComputerRegistry.reset(); ComputerCraft.serverComputerRegistry.reset();
WirelessNetwork.resetNetworks(); WirelessNetwork.resetNetworks();
Tracking.reset(); Tracking.reset();
NetworkUtils.reset();
} }
public static final ResourceLocation LOOT_TREASURE_DISK = new ResourceLocation( ComputerCraft.MOD_ID, "treasure_disk" ); public static final ResourceLocation LOOT_TREASURE_DISK = new ResourceLocation( ComputerCraft.MOD_ID, "treasure_disk" );

View File

@ -12,6 +12,7 @@ import com.google.common.base.CaseFormat;
import com.google.common.base.Converter; import com.google.common.base.Converter;
import dan200.computercraft.ComputerCraft; import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.turtle.event.TurtleAction; import dan200.computercraft.api.turtle.event.TurtleAction;
import dan200.computercraft.core.apis.http.NetworkUtils;
import dan200.computercraft.core.apis.http.options.Action; import dan200.computercraft.core.apis.http.options.Action;
import dan200.computercraft.core.apis.http.options.AddressRuleConfig; import dan200.computercraft.core.apis.http.options.AddressRuleConfig;
import dan200.computercraft.shared.peripheral.monitor.MonitorRenderer; import dan200.computercraft.shared.peripheral.monitor.MonitorRenderer;
@ -56,6 +57,9 @@ public final class Config
private static final ConfigValue<Integer> httpMaxRequests; private static final ConfigValue<Integer> httpMaxRequests;
private static final ConfigValue<Integer> httpMaxWebsockets; private static final ConfigValue<Integer> httpMaxWebsockets;
private static final ConfigValue<Integer> httpDownloadBandwidth;
private static final ConfigValue<Integer> httpUploadBandwidth;
private static final ConfigValue<Boolean> commandBlockEnabled; private static final ConfigValue<Boolean> commandBlockEnabled;
private static final ConfigValue<Integer> modemRange; private static final ConfigValue<Integer> modemRange;
private static final ConfigValue<Integer> modemHighAltitudeRange; private static final ConfigValue<Integer> modemHighAltitudeRange;
@ -187,6 +191,20 @@ public final class Config
.comment( "The number of websockets a computer can have open at one time. Set to 0 for unlimited." ) .comment( "The number of websockets a computer can have open at one time. Set to 0 for unlimited." )
.defineInRange( "max_websockets", ComputerCraft.httpMaxWebsockets, 1, Integer.MAX_VALUE ); .defineInRange( "max_websockets", ComputerCraft.httpMaxWebsockets, 1, Integer.MAX_VALUE );
builder
.comment( "Limits bandwidth used by computers" )
.push( "bandwidth" );
httpDownloadBandwidth = builder
.comment( "The number of bytes which can be downloaded in a second. This is shared across all computers. (bytes/s)" )
.defineInRange( "global_download", ComputerCraft.httpDownloadBandwidth, 1, Integer.MAX_VALUE );
httpUploadBandwidth = builder
.comment( "The number of bytes which can be uploaded in a second. This is shared across all computers. (bytes/s)" )
.defineInRange( "global_upload", ComputerCraft.httpUploadBandwidth, 1, Integer.MAX_VALUE );
builder.pop();
builder.pop(); builder.pop();
} }
@ -329,6 +347,8 @@ public final class Config
ComputerCraft.httpMaxRequests = httpMaxRequests.get(); ComputerCraft.httpMaxRequests = httpMaxRequests.get();
ComputerCraft.httpMaxWebsockets = httpMaxWebsockets.get(); ComputerCraft.httpMaxWebsockets = httpMaxWebsockets.get();
ComputerCraft.httpDownloadBandwidth = httpDownloadBandwidth.get();
NetworkUtils.reloadConfig();
// Peripheral // Peripheral
ComputerCraft.enableCommandBlock = commandBlockEnabled.get(); ComputerCraft.enableCommandBlock = commandBlockEnabled.get();