1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2024-06-22 21:23:21 +00:00

Make many more http options domain-specific

timetout, max_upload, max_download and max_websocket_message may now be
configured on a domain-by-domain basis. This uses the same system that
we use for the block/allow-list from before:

Example:

    [[http.rules]]
        host = "*"
        action = "allow"
	max_upload = 4194304
	max_download = 16777216
	timeout = 30000
This commit is contained in:
SquidDev 2020-05-15 22:44:46 +01:00
parent 4f8217d1ab
commit 7af63d052d
22 changed files with 372 additions and 132 deletions

View File

@ -6,7 +6,8 @@
package dan200.computercraft;
import dan200.computercraft.api.turtle.event.TurtleAction;
import dan200.computercraft.core.apis.http.AddressRule;
import dan200.computercraft.core.apis.http.options.Action;
import dan200.computercraft.core.apis.http.options.AddressRule;
import dan200.computercraft.shared.Config;
import dan200.computercraft.shared.computer.blocks.BlockComputer;
import dan200.computercraft.shared.computer.core.ClientComputerRegistry;
@ -70,7 +71,7 @@ public final class ComputerCraft
public static boolean disable_lua51_features = false;
public static String default_computer_settings = "";
public static boolean debug_enable = true;
public static boolean logPeripheralErrors = true;
public static boolean logComputerErrors = true;
public static boolean commandRequireCreative = true;
public static int computer_threads = 1;
@ -80,16 +81,16 @@ public final class ComputerCraft
public static boolean httpEnabled = true;
public static boolean httpWebsocketEnabled = true;
public static List<AddressRule> httpRules = Collections.unmodifiableList( Stream.concat(
Stream.of( DEFAULT_HTTP_DENY ).map( x -> AddressRule.parse( x, AddressRule.Action.DENY ) ).filter( Objects::nonNull ),
Stream.of( DEFAULT_HTTP_ALLOW ).map( x -> AddressRule.parse( x, AddressRule.Action.ALLOW ) ).filter( Objects::nonNull )
Stream.of( DEFAULT_HTTP_DENY )
.map( x -> AddressRule.parse( x, Action.DENY.toPartial() ) )
.filter( Objects::nonNull ),
Stream.of( DEFAULT_HTTP_ALLOW )
.map( x -> AddressRule.parse( x, Action.ALLOW.toPartial() ) )
.filter( Objects::nonNull )
).collect( Collectors.toList() ) );
public static int httpTimeout = 30000;
public static int httpMaxRequests = 16;
public static long httpMaxDownload = 16 * 1024 * 1024;
public static long httpMaxUpload = 4 * 1024 * 1024;
public static int httpMaxWebsockets = 4;
public static int httpMaxWebsocketMessage = 128 * 1024;
public static boolean enableCommandBlock = false;
public static int modem_range = 64;

View File

@ -119,12 +119,6 @@ public final Object[] request( IArguments args ) throws LuaException
URI uri = HttpRequest.checkUri( address );
HttpRequest request = new HttpRequest( requests, m_apiEnvironment, address, postString, headers, binary, redirect );
long requestBody = request.body().readableBytes() + HttpRequest.getHeaderSize( headers );
if( ComputerCraft.httpMaxUpload != 0 && requestBody > ComputerCraft.httpMaxUpload )
{
throw new HTTPRequestException( "Request body is too large" );
}
// Make the request
request.queue( r -> r.request( uri, httpMethod ) );

View File

@ -7,6 +7,7 @@
import dan200.computercraft.core.apis.IAPIEnvironment;
import java.net.InetSocketAddress;
import java.net.URI;
import java.util.concurrent.Future;
@ -46,7 +47,9 @@ private void doRun()
try
{
NetworkUtils.getAddress( host, 80, false );
InetSocketAddress address = NetworkUtils.getAddress( host, 80, false );
NetworkUtils.getOptions( host, address );
if( tryClose() ) environment.queueEvent( EVENT, address, true );
}
catch( HTTPRequestException e )

View File

@ -6,6 +6,9 @@
package dan200.computercraft.core.apis.http;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.core.apis.http.options.Action;
import dan200.computercraft.core.apis.http.options.AddressRule;
import dan200.computercraft.core.apis.http.options.Options;
import dan200.computercraft.shared.util.ThreadUtils;
import io.netty.buffer.ByteBuf;
import io.netty.channel.EventLoopGroup;
@ -15,7 +18,6 @@
import javax.net.ssl.SSLException;
import javax.net.ssl.TrustManagerFactory;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.security.KeyStore;
import java.util.concurrent.ExecutorService;
@ -106,24 +108,31 @@ public static SslContext getSslContext() throws HTTPRequestException
* @param port The port, or -1 if not defined.
* @param ssl Whether to connect with SSL. This is used to find the default port if not otherwise specified.
* @return The resolved address.
* @throws HTTPRequestException If the host is not permitted.
* @throws HTTPRequestException If the host is not malformed.
*/
public static InetSocketAddress getAddress( String host, int port, boolean ssl ) throws HTTPRequestException
{
if( port < 0 ) port = ssl ? 443 : 80;
InetSocketAddress socketAddress = new InetSocketAddress( host, port );
if( socketAddress.isUnresolved() ) throw new HTTPRequestException( "Unknown host" );
InetAddress address = socketAddress.getAddress();
if( AddressRule.apply( ComputerCraft.httpRules, host, address ) == AddressRule.Action.DENY )
{
throw new HTTPRequestException( "Domain not permitted" );
}
return socketAddress;
}
/**
* Get options for a specific domain.
*
* @param host The host to resolve.
* @param address The address, resolved by {@link #getAddress(String, int, boolean)}.
* @return The options for this host.
* @throws HTTPRequestException If the host is not permitted
*/
public static Options getOptions( String host, InetSocketAddress address ) throws HTTPRequestException
{
Options options = AddressRule.apply( ComputerCraft.httpRules, host, address.getAddress() );
if( options.action == Action.DENY ) throw new HTTPRequestException( "Domain not permitted" );
return options;
}
/**
* Read a {@link ByteBuf} into a byte array.
*

View File

@ -0,0 +1,23 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.core.apis.http.options;
import javax.annotation.Nonnull;
public enum Action
{
ALLOW,
DENY;
private final PartialOptions partial = new PartialOptions( this, null, null, null, null );
@Nonnull
public PartialOptions toPartial()
{
return partial;
}
}

View File

@ -3,11 +3,12 @@
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.core.apis.http;
package dan200.computercraft.core.apis.http.options;
import com.google.common.net.InetAddresses;
import dan200.computercraft.ComputerCraft;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.net.Inet6Address;
import java.net.InetAddress;
@ -18,6 +19,11 @@
*/
public final class AddressRule
{
public static final long MAX_DOWNLOAD = 16 * 1024 * 1024;
public static final long MAX_UPLOAD = 4 * 1024 * 1024;
public static final int TIMEOUT = 30_000;
public static final int WEBSOCKET_MESSAGE = 128 * 1024;
private static final class HostRange
{
private final byte[] min;
@ -44,25 +50,19 @@ public boolean contains( InetAddress address )
}
}
public enum Action
{
ALLOW,
DENY,
}
private final HostRange ip;
private final Pattern domainPattern;
private final Action action;
private final PartialOptions partial;
private AddressRule( HostRange ip, Pattern domainPattern, Action action )
private AddressRule( @Nullable HostRange ip, @Nullable Pattern domainPattern, @Nonnull PartialOptions partial )
{
this.ip = ip;
this.domainPattern = domainPattern;
this.action = action;
this.partial = partial;
}
@Nullable
public static AddressRule parse( String filter, Action action )
public static AddressRule parse( String filter, @Nonnull PartialOptions partial )
{
int cidr = filter.indexOf( '/' );
if( cidr >= 0 )
@ -117,12 +117,12 @@ else if( size < 8 )
size -= 8;
}
return new AddressRule( new HostRange( minBytes, maxBytes ), null, action );
return new AddressRule( new HostRange( minBytes, maxBytes ), null, partial );
}
else
{
Pattern pattern = Pattern.compile( "^\\Q" + filter.replaceAll( "\\*", "\\\\E.*\\\\Q" ) + "\\E$" );
return new AddressRule( null, pattern, action );
return new AddressRule( null, pattern, partial );
}
}
@ -133,7 +133,7 @@ else if( size < 8 )
* @param address The address to check.
* @return Whether it matches any of these patterns.
*/
public boolean matches( String domain, InetAddress address )
private boolean matches( String domain, InetAddress address )
{
if( domainPattern != null )
{
@ -155,13 +155,32 @@ private boolean matchesAddress( InetAddress address )
return ip != null && ip.contains( address );
}
public static Action apply( Iterable<? extends AddressRule> rules, String domain, InetAddress address )
public static Options apply( Iterable<? extends AddressRule> rules, String domain, InetAddress address )
{
PartialOptions options = null;
boolean hasMany = false;
for( AddressRule rule : rules )
{
if( rule.matches( domain, address ) ) return rule.action;
if( !rule.matches( domain, address ) ) continue;
if( options == null )
{
options = rule.partial;
}
else
{
if( !hasMany )
{
options = options.copy();
hasMany = true;
}
options.merge( rule.partial );
}
}
return Action.DENY;
return (options == null ? PartialOptions.DEFAULT : options).toOptions();
}
}

View File

@ -0,0 +1,132 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.core.apis.http.options;
import com.electronwill.nightconfig.core.CommentedConfig;
import com.electronwill.nightconfig.core.Config;
import com.electronwill.nightconfig.core.InMemoryCommentedFormat;
import com.electronwill.nightconfig.core.UnmodifiableConfig;
import dan200.computercraft.ComputerCraft;
import javax.annotation.Nullable;
import java.util.Locale;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
/**
* Parses, checks and generates {@link Config}s for {@link AddressRule}.
*/
public class AddressRuleConfig
{
public static UnmodifiableConfig makeRule( String host, Action action )
{
CommentedConfig config = InMemoryCommentedFormat.defaultInstance().createConfig( ConcurrentHashMap::new );
config.add( "host", host );
config.add( "action", action.name().toLowerCase( Locale.ROOT ) );
if( host.equals( "*" ) && action == Action.ALLOW )
{
config.setComment( "timeout", "The period of time (in milliseconds) to wait before a HTTP request times out. Set to 0 for unlimited." );
config.add( "timeout", AddressRule.TIMEOUT );
config.setComment( "max_download", "The maximum size (in bytes) that a computer can download in a single request. Note that responses may receive more data than allowed, but this data will not be returned to the client." );
config.set( "max_download", AddressRule.MAX_DOWNLOAD );
config.setComment( "max_upload", "The maximum size (in bytes) that a computer can upload in a single request. This includes headers and POST text." );
config.set( "max_upload", AddressRule.MAX_UPLOAD );
config.setComment( "max_websocket_message", "The maximum size (in bytes) that a computer can send or receive in one websocket packet." );
config.set( "max_websocket_message", AddressRule.WEBSOCKET_MESSAGE );
}
return config;
}
public static boolean checkRule( UnmodifiableConfig builder )
{
String hostObj = get( builder, "host", String.class ).orElse( null );
return hostObj != null && checkEnum( builder, "action", Action.class )
&& check( builder, "timeout", Number.class )
&& check( builder, "max_upload", Number.class )
&& check( builder, "max_download", Number.class )
&& check( builder, "websocket_message", Number.class )
&& AddressRule.parse( hostObj, PartialOptions.DEFAULT ) != null;
}
@Nullable
public static AddressRule parseRule( UnmodifiableConfig builder )
{
String hostObj = get( builder, "host", String.class ).orElse( null );
if( hostObj == null ) return null;
Action action = getEnum( builder, "action", Action.class ).orElse( null );
Integer timeout = get( builder, "timeout", Number.class ).map( Number::intValue ).orElse( null );
Long maxUpload = get( builder, "max_upload", Number.class ).map( Number::longValue ).orElse( null );
Long maxDownload = get( builder, "max_download", Number.class ).map( Number::longValue ).orElse( null );
Integer websocketMessage = get( builder, "websocket_message", Number.class ).map( Number::intValue ).orElse( null );
PartialOptions options = new PartialOptions(
action,
maxUpload,
maxDownload,
timeout,
websocketMessage
);
return AddressRule.parse( hostObj, options );
}
private static <T> boolean check( UnmodifiableConfig config, String field, Class<T> klass )
{
Object value = config.get( field );
if( value == null || klass.isInstance( value ) ) return true;
ComputerCraft.log.warn( "HTTP rule's {} is not a {}.", field, klass.getSimpleName() );
return false;
}
private static <T extends Enum<T>> boolean checkEnum( UnmodifiableConfig config, String field, Class<T> klass )
{
Object value = config.get( field );
if( value == null ) return true;
if( !(value instanceof String) )
{
ComputerCraft.log.warn( "HTTP rule's {} is not a string", field );
return false;
}
if( parseEnum( klass, (String) value ) == null )
{
ComputerCraft.log.warn( "HTTP rule's {} is not a known option", field );
return false;
}
return true;
}
private static <T> Optional<T> get( UnmodifiableConfig config, String field, Class<T> klass )
{
Object value = config.get( field );
return klass.isInstance( value ) ? Optional.of( klass.cast( value ) ) : Optional.empty();
}
private static <T extends Enum<T>> Optional<T> getEnum( UnmodifiableConfig config, String field, Class<T> klass )
{
return get( config, field, String.class ).map( x -> parseEnum( klass, x ) );
}
@Nullable
private static <T extends Enum<T>> T parseEnum( Class<T> klass, String x )
{
for( T value : klass.getEnumConstants() )
{
if( value.name().equalsIgnoreCase( x ) ) return value;
}
return null;
}
}

View File

@ -0,0 +1,31 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.core.apis.http.options;
import javax.annotation.Nonnull;
/**
* Options about a specific domain.
*/
public final class Options
{
@Nonnull
public final Action action;
public final long maxUpload;
public final long maxDownload;
public final int timeout;
public final int websocketMessage;
Options( @Nonnull Action action, long maxUpload, long maxDownload, int timeout, int websocketMessage )
{
this.action = action;
this.maxUpload = maxUpload;
this.maxDownload = maxDownload;
this.timeout = timeout;
this.websocketMessage = websocketMessage;
}
}

View File

@ -0,0 +1,59 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.core.apis.http.options;
import javax.annotation.Nonnull;
public final class PartialOptions
{
static final PartialOptions DEFAULT = new PartialOptions( null, null, null, null, null );
Action action;
Long maxUpload;
Long maxDownload;
Integer timeout;
Integer websocketMessage;
Options options;
PartialOptions( Action action, Long maxUpload, Long maxDownload, Integer timeout, Integer websocketMessage )
{
this.action = action;
this.maxUpload = maxUpload;
this.maxDownload = maxDownload;
this.timeout = timeout;
this.websocketMessage = websocketMessage;
}
@Nonnull
Options toOptions()
{
if( options != null ) return options;
return options = new Options(
action == null ? Action.DENY : action,
maxUpload == null ? AddressRule.MAX_UPLOAD : maxUpload,
maxDownload == null ? AddressRule.MAX_DOWNLOAD : maxDownload,
timeout == null ? AddressRule.TIMEOUT : timeout,
websocketMessage == null ? AddressRule.WEBSOCKET_MESSAGE : websocketMessage
);
}
void merge( @Nonnull PartialOptions other )
{
if( action == null && other.action != null ) action = other.action;
if( maxUpload == null && other.maxUpload != null ) maxUpload = other.maxUpload;
if( maxDownload == null && other.maxDownload != null ) maxDownload = other.maxDownload;
if( timeout == null && other.timeout != null ) timeout = other.timeout;
if( websocketMessage == null && other.websocketMessage != null ) websocketMessage = other.websocketMessage;
}
PartialOptions copy()
{
return new PartialOptions( action, maxUpload, maxDownload, timeout, websocketMessage );
}
}

View File

@ -11,6 +11,7 @@
import dan200.computercraft.core.apis.http.NetworkUtils;
import dan200.computercraft.core.apis.http.Resource;
import dan200.computercraft.core.apis.http.ResourceGroup;
import dan200.computercraft.core.apis.http.options.Options;
import dan200.computercraft.core.tracking.TrackingField;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
@ -136,16 +137,24 @@ private void doRequest( URI uri, HttpMethod method )
{
boolean ssl = uri.getScheme().equalsIgnoreCase( "https" );
InetSocketAddress socketAddress = NetworkUtils.getAddress( uri.getHost(), uri.getPort(), ssl );
Options options = NetworkUtils.getOptions( uri.getHost(), socketAddress );
SslContext sslContext = ssl ? NetworkUtils.getSslContext() : null;
// getAddress may have a slight delay, so let's perform another cancellation check.
if( isClosed() ) return;
long requestBody = getHeaderSize( headers ) + postBuffer.capacity();
if( options.maxUpload != 0 && requestBody > options.maxUpload )
{
failure( "Request body is too large" );
return;
}
// Add request size to the tracker before opening the connection
environment.addTrackingChange( TrackingField.HTTP_REQUESTS, 1 );
environment.addTrackingChange( TrackingField.HTTP_UPLOAD, getHeaderSize( headers ) + postBuffer.capacity() );
environment.addTrackingChange( TrackingField.HTTP_UPLOAD, requestBody );
HttpRequestHandler handler = currentRequest = new HttpRequestHandler( this, uri, method );
HttpRequestHandler handler = currentRequest = new HttpRequestHandler( this, uri, method, options );
connectFuture = new Bootstrap()
.group( NetworkUtils.LOOP_GROUP )
.channelFactory( NioSocketChannel::new )
@ -155,9 +164,9 @@ private void doRequest( URI uri, HttpMethod method )
protected void initChannel( SocketChannel ch )
{
if( ComputerCraft.httpTimeout > 0 )
if( options.timeout > 0 )
{
ch.config().setConnectTimeoutMillis( ComputerCraft.httpTimeout );
ch.config().setConnectTimeoutMillis( options.timeout );
}
ChannelPipeline p = ch.pipeline();
@ -166,9 +175,9 @@ protected void initChannel( SocketChannel ch )
p.addLast( sslContext.newHandler( ch.alloc(), uri.getHost(), socketAddress.getPort() ) );
}
if( ComputerCraft.httpTimeout > 0 )
if( options.timeout > 0 )
{
p.addLast( new ReadTimeoutHandler( ComputerCraft.httpTimeout, TimeUnit.MILLISECONDS ) );
p.addLast( new ReadTimeoutHandler( options.timeout, TimeUnit.MILLISECONDS ) );
}
p.addLast(
@ -194,7 +203,7 @@ protected void initChannel( SocketChannel ch )
catch( Exception e )
{
failure( "Could not connect" );
if( ComputerCraft.logPeripheralErrors ) ComputerCraft.log.error( "Error in HTTP request", e );
if( ComputerCraft.logComputerErrors ) ComputerCraft.log.error( "Error in HTTP request", e );
}
}

View File

@ -12,6 +12,7 @@
import dan200.computercraft.core.apis.handles.HandleGeneric;
import dan200.computercraft.core.apis.http.HTTPRequestException;
import dan200.computercraft.core.apis.http.NetworkUtils;
import dan200.computercraft.core.apis.http.options.Options;
import dan200.computercraft.core.tracking.TrackingField;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.CompositeByteBuf;
@ -45,18 +46,20 @@ public final class HttpRequestHandler extends SimpleChannelInboundHandler<HttpOb
private final URI uri;
private final HttpMethod method;
private final Options options;
private Charset responseCharset;
private final HttpHeaders responseHeaders = new DefaultHttpHeaders();
private HttpResponseStatus responseStatus;
private CompositeByteBuf responseBody;
HttpRequestHandler( HttpRequest request, URI uri, HttpMethod method )
HttpRequestHandler( HttpRequest request, URI uri, HttpMethod method, Options options )
{
this.request = request;
this.uri = uri;
this.method = method;
this.options = options;
}
@Override
@ -153,7 +156,7 @@ public void channelRead0( ChannelHandlerContext ctx, HttpObject message )
if( partial.isReadable() )
{
// If we've read more than we're allowed to handle, abort as soon as possible.
if( ComputerCraft.httpMaxDownload != 0 && responseBody.readableBytes() + partial.readableBytes() > ComputerCraft.httpMaxDownload )
if( options.maxDownload != 0 && responseBody.readableBytes() + partial.readableBytes() > options.maxDownload )
{
closed = true;
ctx.close();
@ -185,7 +188,7 @@ public void channelRead0( ChannelHandlerContext ctx, HttpObject message )
@Override
public void exceptionCaught( ChannelHandlerContext ctx, Throwable cause )
{
if( ComputerCraft.logPeripheralErrors ) ComputerCraft.log.error( "Error handling HTTP response", cause );
if( ComputerCraft.logComputerErrors ) ComputerCraft.log.error( "Error handling HTTP response", cause );
request.failure( cause );
}

View File

@ -12,6 +12,7 @@
import dan200.computercraft.core.apis.http.NetworkUtils;
import dan200.computercraft.core.apis.http.Resource;
import dan200.computercraft.core.apis.http.ResourceGroup;
import dan200.computercraft.core.apis.http.options.Options;
import dan200.computercraft.shared.util.IoUtil;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
@ -130,6 +131,7 @@ private void doConnect()
boolean ssl = uri.getScheme().equalsIgnoreCase( "wss" );
InetSocketAddress socketAddress = NetworkUtils.getAddress( uri.getHost(), uri.getPort(), ssl );
Options options = NetworkUtils.getOptions( uri.getHost(), socketAddress );
SslContext sslContext = ssl ? NetworkUtils.getSslContext() : null;
// getAddress may have a slight delay, so let's perform another cancellation check.
@ -151,14 +153,14 @@ protected void initChannel( SocketChannel ch )
WebSocketClientHandshaker handshaker = WebSocketClientHandshakerFactory.newHandshaker(
uri, WebSocketVersion.V13, null, true, headers,
ComputerCraft.httpMaxWebsocketMessage == 0 ? MAX_MESSAGE_SIZE : ComputerCraft.httpMaxWebsocketMessage
options.websocketMessage <= 0 ? MAX_MESSAGE_SIZE : options.websocketMessage
);
p.addLast(
new HttpClientCodec(),
new HttpObjectAggregator( 8192 ),
WebSocketClientCompressionHandler.INSTANCE,
new WebsocketHandler( Websocket.this, handshaker )
new WebsocketHandler( Websocket.this, handshaker, options )
);
}
} )
@ -178,15 +180,15 @@ protected void initChannel( SocketChannel ch )
catch( Exception e )
{
failure( "Could not connect" );
if( ComputerCraft.logPeripheralErrors ) ComputerCraft.log.error( "Error in websocket", e );
if( ComputerCraft.logComputerErrors ) ComputerCraft.log.error( "Error in websocket", e );
}
}
void success( Channel channel )
void success( Channel channel, Options options )
{
if( isClosed() ) return;
WebsocketHandle handle = new WebsocketHandle( this, channel );
WebsocketHandle handle = new WebsocketHandle( this, options, channel );
environment().queueEvent( SUCCESS_EVENT, address, handle );
websocketHandle = createOwnerReference( handle );

View File

@ -6,8 +6,8 @@
package dan200.computercraft.core.apis.http.websocket;
import com.google.common.base.Objects;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.lua.*;
import dan200.computercraft.core.apis.http.options.Options;
import dan200.computercraft.core.tracking.TrackingField;
import dan200.computercraft.shared.util.StringUtil;
import io.netty.buffer.Unpooled;
@ -28,13 +28,15 @@
public class WebsocketHandle implements Closeable
{
private final Websocket websocket;
private final Options options;
private boolean closed = false;
private Channel channel;
public WebsocketHandle( Websocket websocket, Channel channel )
public WebsocketHandle( Websocket websocket, Options options, Channel channel )
{
this.websocket = websocket;
this.options = options;
this.channel = channel;
}
@ -55,7 +57,7 @@ public final void send( IArguments args ) throws LuaException
checkOpen();
String text = StringUtil.toString( args.get( 0 ) );
if( ComputerCraft.httpMaxWebsocketMessage != 0 && text.length() > ComputerCraft.httpMaxWebsocketMessage )
if( options.websocketMessage != 0 && text.length() > options.websocketMessage )
{
throw new LuaException( "Message is too large" );
}

View File

@ -7,6 +7,7 @@
import dan200.computercraft.core.apis.http.HTTPRequestException;
import dan200.computercraft.core.apis.http.NetworkUtils;
import dan200.computercraft.core.apis.http.options.Options;
import dan200.computercraft.core.tracking.TrackingField;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ConnectTimeoutException;
@ -23,11 +24,13 @@ public class WebsocketHandler extends SimpleChannelInboundHandler<Object>
{
private final Websocket websocket;
private final WebSocketClientHandshaker handshaker;
private final Options options;
public WebsocketHandler( Websocket websocket, WebSocketClientHandshaker handshaker )
public WebsocketHandler( Websocket websocket, WebSocketClientHandshaker handshaker, Options options )
{
this.handshaker = handshaker;
this.websocket = websocket;
this.options = options;
}
@Override
@ -52,7 +55,7 @@ public void channelRead0( ChannelHandlerContext ctx, Object msg )
if( !handshaker.isHandshakeComplete() )
{
handshaker.finishHandshake( ctx.channel(), (FullHttpResponse) msg );
websocket.success( ctx.channel() );
websocket.success( ctx.channel(), options );
return;
}

View File

@ -518,7 +518,7 @@ public void run()
private static void timeoutTask( ComputerExecutor executor, Thread thread, long time )
{
if( !ComputerCraft.logPeripheralErrors ) return;
if( !ComputerCraft.logComputerErrors ) return;
StringBuilder builder = new StringBuilder()
.append( "Terminating computer #" ).append( executor.getComputer().getID() )

View File

@ -54,7 +54,7 @@ public Varargs invoke( LuaState luaState, Varargs args ) throws LuaError
}
catch( Throwable t )
{
if( ComputerCraft.logPeripheralErrors )
if( ComputerCraft.logComputerErrors )
{
ComputerCraft.log.error( "Error calling " + name + " on " + instance, t );
}

View File

@ -323,7 +323,7 @@ private LuaValue toValue( @Nullable Object object, @Nullable Map<Object, LuaValu
return wrapped;
}
if( ComputerCraft.logPeripheralErrors )
if( ComputerCraft.logComputerErrors )
{
ComputerCraft.log.warn( "Received unknown type '{}', returning nil.", object.getClass().getName() );
}
@ -532,7 +532,7 @@ public long issueMainThreadTask( @Nonnull final ILuaTask task ) throws LuaExcept
}
catch( Throwable t )
{
if( ComputerCraft.logPeripheralErrors ) ComputerCraft.log.error( "Error running task", t );
if( ComputerCraft.logComputerErrors ) ComputerCraft.log.error( "Error running task", t );
m_computer.queueEvent( "task_complete", new Object[] {
taskID, false, "Java Exception Thrown: " + t,
} );

View File

@ -64,7 +64,7 @@ protected Varargs invoke( LuaState state, DebugFrame debugFrame, Varargs args )
}
catch( Throwable t )
{
if( ComputerCraft.logPeripheralErrors )
if( ComputerCraft.logComputerErrors )
{
ComputerCraft.log.error( "Error calling " + name + " on " + instance, t );
}
@ -95,7 +95,7 @@ protected Varargs resumeThis( LuaState state, Container container, Varargs args
}
catch( Throwable t )
{
if( ComputerCraft.logPeripheralErrors )
if( ComputerCraft.logComputerErrors )
{
ComputerCraft.log.error( "Error calling " + name + " on " + container.callback, t );
}

View File

@ -12,8 +12,8 @@
import com.google.common.base.Converter;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.turtle.event.TurtleAction;
import dan200.computercraft.core.apis.http.AddressRule;
import dan200.computercraft.core.apis.http.websocket.Websocket;
import dan200.computercraft.core.apis.http.options.Action;
import dan200.computercraft.core.apis.http.options.AddressRuleConfig;
import dan200.computercraft.shared.peripheral.monitor.MonitorRenderer;
import net.minecraftforge.common.ForgeConfigSpec;
import net.minecraftforge.eventbus.api.SubscribeEvent;
@ -21,7 +21,6 @@
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.config.ModConfig;
import javax.annotation.Nullable;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
@ -55,12 +54,8 @@ public final class Config
private static final ConfigValue<Boolean> httpWebsocketEnabled;
private static final ConfigValue<List<? extends UnmodifiableConfig>> httpRules;
private static final ConfigValue<Integer> httpTimeout;
private static final ConfigValue<Integer> httpMaxRequests;
private static final ConfigValue<Integer> httpMaxDownload;
private static final ConfigValue<Integer> httpMaxUpload;
private static final ConfigValue<Integer> httpMaxWebsockets;
private static final ConfigValue<Integer> httpMaxWebsocketMessage;
private static final ConfigValue<Boolean> commandBlockEnabled;
private static final ConfigValue<Integer> modemRange;
@ -121,7 +116,7 @@ private Config() {}
logComputerErrors = builder
.comment( "Log exceptions thrown by peripherals and other Lua objects.\n" +
"This makes it easier for mod authors to debug problems, but may result in log spam should people use buggy methods." )
.define( "log_computer_errors", ComputerCraft.logPeripheralErrors );
.define( "log_computer_errors", ComputerCraft.logComputerErrors );
}
{
@ -164,42 +159,25 @@ private Config() {}
.define( "websocket_enabled", ComputerCraft.httpWebsocketEnabled );
httpRules = builder
.comment( "A list of rules which control which domains or IPs are allowed through the \"http\" API on computers.\n" +
"Each rule is an item with a 'host' to match against, and an action. " +
.comment( "A list of rules which control behaviour of the \"http\" API for specific domains or IPs.\n" +
"Each rule is an item with a 'host' to match against, and a series of properties. " +
"The host may be a domain name (\"pastebin.com\"),\n" +
"wildcard (\"*.pastebin.com\") or CIDR notation (\"127.0.0.0/8\"). 'action' maybe 'allow' or 'block'. If no rules" +
"match, the domain will be blocked." )
"wildcard (\"*.pastebin.com\") or CIDR notation (\"127.0.0.0/8\"). If no rules, the domain is blocked." )
.defineList( "rules",
Stream.concat(
Stream.of( ComputerCraft.DEFAULT_HTTP_DENY ).map( x -> makeRule( x, "deny" ) ),
Stream.of( ComputerCraft.DEFAULT_HTTP_ALLOW ).map( x -> makeRule( x, "allow" ) )
Stream.of( ComputerCraft.DEFAULT_HTTP_DENY ).map( x -> AddressRuleConfig.makeRule( x, Action.DENY ) ),
Stream.of( ComputerCraft.DEFAULT_HTTP_ALLOW ).map( x -> AddressRuleConfig.makeRule( x, Action.ALLOW ) )
).collect( Collectors.toList() ),
x -> x instanceof UnmodifiableConfig && parseRule( (UnmodifiableConfig) x ) != null );
httpTimeout = builder
.comment( "The period of time (in milliseconds) to wait before a HTTP request times out. Set to 0 for unlimited." )
.defineInRange( "timeout", ComputerCraft.httpTimeout, 0, Integer.MAX_VALUE );
x -> x instanceof UnmodifiableConfig && AddressRuleConfig.checkRule( (UnmodifiableConfig) x ) );
httpMaxRequests = builder
.comment( "The number of http requests a computer can make at one time. Additional requests will be queued, and sent when the running requests have finished. Set to 0 for unlimited." )
.defineInRange( "max_requests", ComputerCraft.httpMaxRequests, 0, Integer.MAX_VALUE );
httpMaxDownload = builder
.comment( "The maximum size (in bytes) that a computer can download in a single request. Note that responses may receive more data than allowed, but this data will not be returned to the client." )
.defineInRange( "max_download", (int) ComputerCraft.httpMaxDownload, 0, Integer.MAX_VALUE );
httpMaxUpload = builder
.comment( "The maximum size (in bytes) that a computer can upload in a single request. This includes headers and POST text." )
.defineInRange( "max_upload", (int) ComputerCraft.httpMaxUpload, 0, Integer.MAX_VALUE );
httpMaxWebsockets = builder
.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 );
httpMaxWebsocketMessage = builder
.comment( "The maximum size (in bytes) that a computer can send or receive in one websocket packet." )
.defineInRange( "max_websocket_message", ComputerCraft.httpMaxWebsocketMessage, 0, Websocket.MAX_MESSAGE_SIZE );
builder.pop();
}
@ -291,7 +269,7 @@ public static void sync()
ComputerCraft.default_computer_settings = defaultComputerSettings.get();
ComputerCraft.debug_enable = debugEnabled.get();
ComputerCraft.computer_threads = computerThreads.get();
ComputerCraft.logPeripheralErrors = logComputerErrors.get();
ComputerCraft.logComputerErrors = logComputerErrors.get();
// Execution
ComputerCraft.computer_threads = computerThreads.get();
@ -302,14 +280,10 @@ public static void sync()
ComputerCraft.httpEnabled = httpEnabled.get();
ComputerCraft.httpWebsocketEnabled = httpWebsocketEnabled.get();
ComputerCraft.httpRules = Collections.unmodifiableList( httpRules.get().stream()
.map( Config::parseRule ).filter( Objects::nonNull ).collect( Collectors.toList() ) );
.map( AddressRuleConfig::parseRule ).filter( Objects::nonNull ).collect( Collectors.toList() ) );
ComputerCraft.httpTimeout = httpTimeout.get();
ComputerCraft.httpMaxRequests = httpMaxRequests.get();
ComputerCraft.httpMaxDownload = httpMaxDownload.get();
ComputerCraft.httpMaxUpload = httpMaxUpload.get();
ComputerCraft.httpMaxWebsockets = httpMaxWebsockets.get();
ComputerCraft.httpMaxWebsocketMessage = httpMaxWebsocketMessage.get();
// Peripheral
ComputerCraft.enableCommandBlock = commandBlockEnabled.get();
@ -362,28 +336,4 @@ private static TurtleAction getAction( String value )
return null;
}
}
private static UnmodifiableConfig makeRule( String host, String action )
{
com.electronwill.nightconfig.core.Config config = com.electronwill.nightconfig.core.Config.inMemory();
config.add( "host", host );
config.add( "action", action );
return config;
}
@Nullable
private static AddressRule parseRule( UnmodifiableConfig builder )
{
Object hostObj = builder.get( "host" );
Object actionObj = builder.get( "action" );
if( !(hostObj instanceof String) || !(actionObj instanceof String) ) return null;
String host = (String) hostObj, action = (String) actionObj;
for( AddressRule.Action candiate : AddressRule.Action.values() )
{
if( candiate.name().equalsIgnoreCase( action ) ) return AddressRule.parse( host, candiate );
}
return null;
}
}

View File

@ -64,7 +64,7 @@ private Object[] doCommand( String command )
}
catch( Throwable t )
{
if( ComputerCraft.logPeripheralErrors ) ComputerCraft.log.error( "Error running command.", t );
if( ComputerCraft.logComputerErrors ) ComputerCraft.log.error( "Error running command.", t );
return new Object[] { false, createOutput( "Java Exception Thrown: " + t ) };
}
}

View File

@ -85,7 +85,7 @@ public class ComputerTestDelegate
@BeforeEach
public void before() throws IOException
{
ComputerCraft.logPeripheralErrors = true;
ComputerCraft.logComputerErrors = true;
if( REPORT_PATH.delete() ) ComputerCraft.log.info( "Deleted previous coverage report." );

View File

@ -42,7 +42,7 @@ public static void run( String program, int maxTimes )
public static void run( IWritableMount mount, Consumer<Computer> setup, int maxTicks )
{
ComputerCraft.logPeripheralErrors = true;
ComputerCraft.logComputerErrors = true;
ComputerCraft.maxMainComputerTime = ComputerCraft.maxMainGlobalTime = Integer.MAX_VALUE;
Terminal term = new Terminal( ComputerCraft.terminalWidth_computer, ComputerCraft.terminalHeight_computer );