mirror of
				https://github.com/SquidDev-CC/CC-Tweaked
				synced 2025-10-31 05:33:00 +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:
		| @@ -15,6 +15,7 @@ import dan200.computercraft.api.lua.LuaException; | ||||
| import dan200.computercraft.core.apis.IAPIEnvironment; | ||||
| import dan200.computercraft.core.apis.handles.BinaryInputHandle; | ||||
| import dan200.computercraft.core.apis.handles.EncodedInputHandle; | ||||
| import dan200.computercraft.core.tracking.TrackingField; | ||||
|  | ||||
| import javax.annotation.Nonnull; | ||||
| import java.io.*; | ||||
| @@ -101,7 +102,7 @@ public class HTTPRequest implements Runnable | ||||
|         { | ||||
|             // Queue the failure event if not. | ||||
|             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; | ||||
|         } | ||||
|  | ||||
| @@ -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 | ||||
|             if( m_postText != null ) | ||||
|             { | ||||
| @@ -178,6 +184,9 @@ public class HTTPRequest implements Runnable | ||||
|                 headers.put( header.getKey(), joiner.join( header.getValue() ) ); | ||||
|             } | ||||
|  | ||||
|             m_environment.addTrackingChange( TrackingField.HTTP_DOWNLOAD, | ||||
|                 getHeaderSize( connection.getHeaderFields() ) + result.length ); | ||||
|  | ||||
|             InputStream contents = new ByteArrayInputStream( result ); | ||||
|             ILuaObject stream = wrapStream( | ||||
|                 m_binary ? new BinaryInputHandle( contents ) : new EncodedInputHandle( contents, connection.getContentEncoding() ), | ||||
| @@ -189,17 +198,17 @@ public class HTTPRequest implements Runnable | ||||
|             // Queue the appropriate event. | ||||
|             if( responseSuccess ) | ||||
|             { | ||||
|                 m_environment.queueEvent( "http_success", new Object[] { m_urlString, stream } ); | ||||
|                 m_environment.queueEvent( "http_success", new Object[]{ m_urlString, stream } ); | ||||
|             } | ||||
|             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 ) | ||||
|         { | ||||
|             // 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 String[] newMethods = Arrays.copyOf( oldMethods, oldMethods.length + 2 ); | ||||
|         newMethods[ methodOffset + 0 ] = "getResponseCode"; | ||||
|         newMethods[ methodOffset + 1 ] = "getResponseHeaders"; | ||||
|         newMethods[methodOffset + 0] = "getResponseCode"; | ||||
|         newMethods[methodOffset + 1] = "getResponseHeaders"; | ||||
|  | ||||
|         return new ILuaObject() | ||||
|         { | ||||
| @@ -233,12 +242,12 @@ public class HTTPRequest implements Runnable | ||||
|                     case 0: | ||||
|                     { | ||||
|                         // getResponseCode | ||||
|                         return new Object[] { responseCode }; | ||||
|                         return new Object[]{ responseCode }; | ||||
|                     } | ||||
|                     case 1: | ||||
|                     { | ||||
|                         // getResponseHeaders | ||||
|                         return new Object[] { responseHeaders }; | ||||
|                         return new Object[]{ responseHeaders }; | ||||
|                     } | ||||
|                     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; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -12,6 +12,7 @@ import dan200.computercraft.api.lua.ILuaObject; | ||||
| import dan200.computercraft.api.lua.LuaException; | ||||
| import dan200.computercraft.core.apis.HTTPAPI; | ||||
| import dan200.computercraft.core.apis.IAPIEnvironment; | ||||
| import dan200.computercraft.core.tracking.TrackingField; | ||||
| import io.netty.buffer.ByteBuf; | ||||
| import io.netty.channel.Channel; | ||||
| import io.netty.channel.ChannelHandlerContext; | ||||
| @@ -71,7 +72,7 @@ public class WebsocketConnection extends SimpleChannelInboundHandler<Object> imp | ||||
|     private void onClosed() | ||||
|     { | ||||
|         close( true ); | ||||
|         computer.queueEvent( CLOSE_EVENT, new Object[] { url } ); | ||||
|         computer.queueEvent( CLOSE_EVENT, new Object[]{ url } ); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
| @@ -102,7 +103,7 @@ public class WebsocketConnection extends SimpleChannelInboundHandler<Object> imp | ||||
|         if( !handshaker.isHandshakeComplete() ) | ||||
|         { | ||||
|             handshaker.finishHandshake( ch, (FullHttpResponse) msg ); | ||||
|             computer.queueEvent( SUCCESS_EVENT, new Object[] { url, this } ); | ||||
|             computer.queueEvent( SUCCESS_EVENT, new Object[]{ url, this } ); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
| @@ -115,14 +116,19 @@ public class WebsocketConnection extends SimpleChannelInboundHandler<Object> imp | ||||
|         WebSocketFrame frame = (WebSocketFrame) msg; | ||||
|         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 ) | ||||
|         { | ||||
|             ByteBuf data = frame.content(); | ||||
|             byte[] converted = new byte[ data.readableBytes() ]; | ||||
|             byte[] converted = new byte[data.readableBytes()]; | ||||
|             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 ) | ||||
|         { | ||||
| @@ -135,7 +141,7 @@ public class WebsocketConnection extends SimpleChannelInboundHandler<Object> imp | ||||
|     public void exceptionCaught( ChannelHandlerContext ctx, Throwable cause ) | ||||
|     { | ||||
|         ctx.close(); | ||||
|         computer.queueEvent( FAILURE_EVENT, new Object[] { | ||||
|         computer.queueEvent( FAILURE_EVENT, new Object[]{ | ||||
|             url, | ||||
|             cause instanceof WebSocketHandshakeException ? cause.getMessage() : "Could not connect" | ||||
|         } ); | ||||
| @@ -145,7 +151,7 @@ public class WebsocketConnection extends SimpleChannelInboundHandler<Object> imp | ||||
|     @Override | ||||
|     public String[] getMethodNames() | ||||
|     { | ||||
|         return new String[] { "receive", "send", "close" }; | ||||
|         return new String[]{ "receive", "send", "close" }; | ||||
|     } | ||||
|  | ||||
|     @Nullable | ||||
| @@ -159,15 +165,16 @@ public class WebsocketConnection extends SimpleChannelInboundHandler<Object> imp | ||||
|                 { | ||||
|                     checkOpen(); | ||||
|                     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: | ||||
|             { | ||||
|                 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 ) ); | ||||
|                 return null; | ||||
|             } | ||||
|   | ||||
| @@ -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_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 FS_OPS = TrackingField.of( "fs", "Filesystem operations", x -> String.format( "%6d", x ) ); | ||||
|     public static final TrackingField TURTLE_OPS = TrackingField.of( "turtle", "Turtle operations", 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", TrackingField::formatDefault ); | ||||
|     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 displayName; | ||||
| @@ -58,4 +65,24 @@ public class TrackingField | ||||
|     { | ||||
|         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 ) ); | ||||
|     } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 SquidDev
					SquidDev