1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-05-04 16:34:14 +00:00

Add some basic tracking for HTTP requests and websockets

This should allow for easier identification of misbehaving computers,
which are consuming a large amount of bandwidth.
This commit is contained in:
SquidDev 2018-05-14 15:52:16 +01:00
parent 8775052dee
commit b522af3075
3 changed files with 75 additions and 21 deletions

View File

@ -15,6 +15,7 @@ import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.core.apis.IAPIEnvironment; import dan200.computercraft.core.apis.IAPIEnvironment;
import dan200.computercraft.core.apis.handles.BinaryInputHandle; import dan200.computercraft.core.apis.handles.BinaryInputHandle;
import dan200.computercraft.core.apis.handles.EncodedInputHandle; import dan200.computercraft.core.apis.handles.EncodedInputHandle;
import dan200.computercraft.core.tracking.TrackingField;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.io.*; import java.io.*;
@ -101,7 +102,7 @@ public class HTTPRequest implements Runnable
{ {
// Queue the failure event if not. // Queue the failure event if not.
String error = e.getMessage(); String error = e.getMessage();
m_environment.queueEvent( "http_failure", new Object[] { m_urlString, error == null ? "Could not connect" : error, null } ); m_environment.queueEvent( "http_failure", new Object[]{ m_urlString, error == null ? "Could not connect" : error, null } );
return; return;
} }
@ -134,6 +135,11 @@ public class HTTPRequest implements Runnable
} }
} }
// Add request size and count to the tracker before opening the connection
m_environment.addTrackingChange( TrackingField.HTTP_REQUESTS );
m_environment.addTrackingChange( TrackingField.HTTP_UPLOAD,
getHeaderSize( connection.getRequestProperties() ) + (m_postText == null ? 0 : m_postText.length()) );
// Send POST text // Send POST text
if( m_postText != null ) if( m_postText != null )
{ {
@ -178,6 +184,9 @@ public class HTTPRequest implements Runnable
headers.put( header.getKey(), joiner.join( header.getValue() ) ); headers.put( header.getKey(), joiner.join( header.getValue() ) );
} }
m_environment.addTrackingChange( TrackingField.HTTP_DOWNLOAD,
getHeaderSize( connection.getHeaderFields() ) + result.length );
InputStream contents = new ByteArrayInputStream( result ); InputStream contents = new ByteArrayInputStream( result );
ILuaObject stream = wrapStream( ILuaObject stream = wrapStream(
m_binary ? new BinaryInputHandle( contents ) : new EncodedInputHandle( contents, connection.getContentEncoding() ), m_binary ? new BinaryInputHandle( contents ) : new EncodedInputHandle( contents, connection.getContentEncoding() ),
@ -189,17 +198,17 @@ public class HTTPRequest implements Runnable
// Queue the appropriate event. // Queue the appropriate event.
if( responseSuccess ) if( responseSuccess )
{ {
m_environment.queueEvent( "http_success", new Object[] { m_urlString, stream } ); m_environment.queueEvent( "http_success", new Object[]{ m_urlString, stream } );
} }
else else
{ {
m_environment.queueEvent( "http_failure", new Object[] { m_urlString, "Could not connect", stream } ); m_environment.queueEvent( "http_failure", new Object[]{ m_urlString, "Could not connect", stream } );
} }
} }
catch( IOException e ) catch( IOException e )
{ {
// There was an error // There was an error
m_environment.queueEvent( "http_failure", new Object[] { m_urlString, "Could not connect", null } ); m_environment.queueEvent( "http_failure", new Object[]{ m_urlString, "Could not connect", null } );
} }
} }
@ -209,8 +218,8 @@ public class HTTPRequest implements Runnable
final int methodOffset = oldMethods.length; final int methodOffset = oldMethods.length;
final String[] newMethods = Arrays.copyOf( oldMethods, oldMethods.length + 2 ); final String[] newMethods = Arrays.copyOf( oldMethods, oldMethods.length + 2 );
newMethods[ methodOffset + 0 ] = "getResponseCode"; newMethods[methodOffset + 0] = "getResponseCode";
newMethods[ methodOffset + 1 ] = "getResponseHeaders"; newMethods[methodOffset + 1] = "getResponseHeaders";
return new ILuaObject() return new ILuaObject()
{ {
@ -233,12 +242,12 @@ public class HTTPRequest implements Runnable
case 0: case 0:
{ {
// getResponseCode // getResponseCode
return new Object[] { responseCode }; return new Object[]{ responseCode };
} }
case 1: case 1:
{ {
// getResponseHeaders // getResponseHeaders
return new Object[] { responseHeaders }; return new Object[]{ responseHeaders };
} }
default: default:
{ {
@ -248,4 +257,15 @@ public class HTTPRequest implements Runnable
} }
}; };
} }
private static long getHeaderSize( Map<String, List<String>> headers )
{
long size = 0;
for( Map.Entry<String, List<String>> header : headers.entrySet() )
{
size += header.getKey() == null ? 0 : header.getKey().length();
for( String value : header.getValue() ) size += value == null ? 0 : value.length() + 1;
}
return size;
}
} }

View File

@ -12,6 +12,7 @@ import dan200.computercraft.api.lua.ILuaObject;
import dan200.computercraft.api.lua.LuaException; import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.core.apis.HTTPAPI; import dan200.computercraft.core.apis.HTTPAPI;
import dan200.computercraft.core.apis.IAPIEnvironment; import dan200.computercraft.core.apis.IAPIEnvironment;
import dan200.computercraft.core.tracking.TrackingField;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
@ -71,7 +72,7 @@ public class WebsocketConnection extends SimpleChannelInboundHandler<Object> imp
private void onClosed() private void onClosed()
{ {
close( true ); close( true );
computer.queueEvent( CLOSE_EVENT, new Object[] { url } ); computer.queueEvent( CLOSE_EVENT, new Object[]{ url } );
} }
@Override @Override
@ -102,7 +103,7 @@ public class WebsocketConnection extends SimpleChannelInboundHandler<Object> imp
if( !handshaker.isHandshakeComplete() ) if( !handshaker.isHandshakeComplete() )
{ {
handshaker.finishHandshake( ch, (FullHttpResponse) msg ); handshaker.finishHandshake( ch, (FullHttpResponse) msg );
computer.queueEvent( SUCCESS_EVENT, new Object[] { url, this } ); computer.queueEvent( SUCCESS_EVENT, new Object[]{ url, this } );
return; return;
} }
@ -115,14 +116,19 @@ public class WebsocketConnection extends SimpleChannelInboundHandler<Object> imp
WebSocketFrame frame = (WebSocketFrame) msg; WebSocketFrame frame = (WebSocketFrame) msg;
if( frame instanceof TextWebSocketFrame ) if( frame instanceof TextWebSocketFrame )
{ {
computer.queueEvent( MESSAGE_EVENT, new Object[] { url, ((TextWebSocketFrame) frame).text() } ); String data = ((TextWebSocketFrame) frame).text();
computer.addTrackingChange( TrackingField.WEBSOCKET_INCOMING, data.length() );
computer.queueEvent( MESSAGE_EVENT, new Object[]{ url, data } );
} }
else if( frame instanceof BinaryWebSocketFrame ) else if( frame instanceof BinaryWebSocketFrame )
{ {
ByteBuf data = frame.content(); ByteBuf data = frame.content();
byte[] converted = new byte[ data.readableBytes() ]; byte[] converted = new byte[data.readableBytes()];
data.readBytes( converted ); data.readBytes( converted );
computer.queueEvent( MESSAGE_EVENT, new Object[] { url, data } );
computer.addTrackingChange( TrackingField.WEBSOCKET_INCOMING, converted.length );
computer.queueEvent( MESSAGE_EVENT, new Object[]{ url, converted } );
} }
else if( frame instanceof CloseWebSocketFrame ) else if( frame instanceof CloseWebSocketFrame )
{ {
@ -135,7 +141,7 @@ public class WebsocketConnection extends SimpleChannelInboundHandler<Object> imp
public void exceptionCaught( ChannelHandlerContext ctx, Throwable cause ) public void exceptionCaught( ChannelHandlerContext ctx, Throwable cause )
{ {
ctx.close(); ctx.close();
computer.queueEvent( FAILURE_EVENT, new Object[] { computer.queueEvent( FAILURE_EVENT, new Object[]{
url, url,
cause instanceof WebSocketHandshakeException ? cause.getMessage() : "Could not connect" cause instanceof WebSocketHandshakeException ? cause.getMessage() : "Could not connect"
} ); } );
@ -145,7 +151,7 @@ public class WebsocketConnection extends SimpleChannelInboundHandler<Object> imp
@Override @Override
public String[] getMethodNames() public String[] getMethodNames()
{ {
return new String[] { "receive", "send", "close" }; return new String[]{ "receive", "send", "close" };
} }
@Nullable @Nullable
@ -159,15 +165,16 @@ public class WebsocketConnection extends SimpleChannelInboundHandler<Object> imp
{ {
checkOpen(); checkOpen();
Object[] event = context.pullEvent( MESSAGE_EVENT ); Object[] event = context.pullEvent( MESSAGE_EVENT );
if( event.length >= 3 && Objects.equal( event[ 1 ], url ) ) if( event.length >= 3 && Objects.equal( event[1], url ) )
{ {
return new Object[] { event[ 2 ] }; return new Object[]{ event[2] };
} }
} }
case 1: case 1:
{ {
checkOpen(); checkOpen();
String text = arguments.length > 0 && arguments[ 0 ] != null ? arguments[ 0 ].toString() : ""; String text = arguments.length > 0 && arguments[0] != null ? arguments[0].toString() : "";
computer.addTrackingChange( TrackingField.WEBSOCKET_OUTGOING, text.length() );
channel.writeAndFlush( new TextWebSocketFrame( text ) ); channel.writeAndFlush( new TextWebSocketFrame( text ) );
return null; return null;
} }

View File

@ -17,9 +17,16 @@ public class TrackingField
public static final TrackingField SERVER_COUNT = TrackingField.of( "server_count", "Server task count", x -> String.format( "%4d", x ) ); public static final TrackingField SERVER_COUNT = TrackingField.of( "server_count", "Server task count", x -> String.format( "%4d", x ) );
public static final TrackingField SERVER_TIME = TrackingField.of( "server_time", "Server task time", x -> String.format( "%7.1fms", x / 1e6 ) ); public static final TrackingField SERVER_TIME = TrackingField.of( "server_time", "Server task time", x -> String.format( "%7.1fms", x / 1e6 ) );
public static final TrackingField PERIPHERAL_OPS = TrackingField.of( "peripheral", "Peripheral calls", x -> String.format( "%6d", x ) ); public static final TrackingField PERIPHERAL_OPS = TrackingField.of( "peripheral", "Peripheral calls", TrackingField::formatDefault );
public static final TrackingField FS_OPS = TrackingField.of( "fs", "Filesystem operations", x -> String.format( "%6d", x ) ); public static final TrackingField FS_OPS = TrackingField.of( "fs", "Filesystem operations", TrackingField::formatDefault );
public static final TrackingField TURTLE_OPS = TrackingField.of( "turtle", "Turtle operations", x -> String.format( "%6d", x ) ); public static final TrackingField TURTLE_OPS = TrackingField.of( "turtle", "Turtle operations", TrackingField::formatDefault );
public static final TrackingField HTTP_REQUESTS = TrackingField.of( "http", "HTTP requests", TrackingField::formatDefault );
public static final TrackingField HTTP_UPLOAD = TrackingField.of( "http_upload", "HTTP upload", TrackingField::formatBytes );
public static final TrackingField HTTP_DOWNLOAD = TrackingField.of( "http_download", "HTTT download", TrackingField::formatBytes );
public static final TrackingField WEBSOCKET_INCOMING = TrackingField.of( "websocket_incoming", "Websocket incoming", TrackingField::formatBytes );
public static final TrackingField WEBSOCKET_OUTGOING = TrackingField.of( "websocket_outgoing", "Websocket outgoing", TrackingField::formatBytes );
private final String id; private final String id;
private final String displayName; private final String displayName;
@ -58,4 +65,24 @@ public class TrackingField
{ {
return Collections.unmodifiableMap( fields ); return Collections.unmodifiableMap( fields );
} }
private static String formatDefault( long value )
{
return String.format( "%6d", value );
}
/**
* So technically a kibibyte, but let's not argue here.
*/
private static final int KILOBYTE_SIZE = 1024;
private static final String SI_PREFIXES = "KMGT";
private static String formatBytes( long bytes )
{
if( bytes < 1024 ) return String.format( "%10d B", bytes );
int exp = (int) (Math.log( bytes ) / Math.log( KILOBYTE_SIZE ));
if( exp > SI_PREFIXES.length() ) exp = SI_PREFIXES.length();
return String.format( "%10.1f %siB", bytes / Math.pow( KILOBYTE_SIZE, exp ), SI_PREFIXES.charAt( exp - 1 ) );
}
} }