diff --git a/G-Earth/src/main/java/gearth/services/extension_handler/extensions/implementations/network/NetworkExtensionsProducer.java b/G-Earth/src/main/java/gearth/services/extension_handler/extensions/implementations/network/NetworkExtensionsProducer.java index f583d30..c4cb89b 100644 --- a/G-Earth/src/main/java/gearth/services/extension_handler/extensions/implementations/network/NetworkExtensionsProducer.java +++ b/G-Earth/src/main/java/gearth/services/extension_handler/extensions/implementations/network/NetworkExtensionsProducer.java @@ -2,85 +2,125 @@ package gearth.services.extension_handler.extensions.implementations.network; import gearth.protocol.HPacket; import gearth.services.extension_handler.extensions.extensionproducers.ExtensionProducer; +import gearth.services.extension_handler.extensions.extensionproducers.ExtensionProducerFactory; import gearth.services.extension_handler.extensions.extensionproducers.ExtensionProducerObserver; import gearth.services.extension_handler.extensions.implementations.network.authentication.Authenticator; import java.io.DataInputStream; import java.io.IOException; -import java.io.InputStream; import java.net.ServerSocket; import java.net.Socket; /** + * Represents an {@link ExtensionProducer} that implements a server to which + * remotely-ran extensions can connect. + * + * @see ExtensionProducerFactory#getAll() for instance creation. + * * Created by Jonas on 21/06/18. */ -public class NetworkExtensionsProducer implements ExtensionProducer { +public final class NetworkExtensionsProducer implements ExtensionProducer { + /** + * Initial port server tries to listen at, if {@link ServerSocket} creation fails, + * it tries next port. + */ + private static final int PORT_ONSET = 9092; + + /** + * Represents the number of bytes per boolean encoded in an incoming packet. + */ + private static final int BOOLEAN_SIZE = 1; + + /** + * Represents the maximum number of bytes per string encoded in an incoming packet. + */ + private static final int MAX_STRING_SIZE = Character.BYTES * 100; + + /** + * Represents the maximum number of bytes of an incoming packet. + *

+ * Used as a form of validation for packets, prevents other Apps that connect + * with the server from sending unexpected data and inexplicably causing huge byte array allocations. + *

+ * Since the server only accepts {@link NetworkExtensionInfo.INCOMING_MESSAGES_IDS#EXTENSIONINFO} packets, + * this value is calculated based on that packet. + */ + private static final int MAX_PACKET_SIZE = (MAX_STRING_SIZE * 6) + (BOOLEAN_SIZE * 4); + + /** + * The port at which the {@link #serverSocket} is listening for incoming connections. + */ public static int extensionPort = -1; - private ServerSocket serverSocket; @Override public void startProducing(ExtensionProducerObserver observer) { -// serverSocket = new ServerSocket(0); - int port = 9092; - boolean serverSetup = false; - while (!serverSetup) { - serverSetup = createServer(port); - port++; - } + /* + Initialise the serverSocket at the argued port. + */ + int port = PORT_ONSET; + while (!createServer(port)) + ++port; + /* + Start connection listener thread. + */ new Thread(() -> { + try { + while (!serverSocket.isClosed()) { - Socket extensionSocket = serverSocket.accept(); + + // accept a new connection + final Socket extensionSocket = serverSocket.accept(); extensionSocket.setTcpNoDelay(true); + /* + Start client session handler thread. + */ new Thread(() -> { + try { + + // write INFOREQUEST packet to client synchronized (extensionSocket) { extensionSocket.getOutputStream().write((new HPacket(NetworkExtensionInfo.OUTGOING_MESSAGES_IDS.INFOREQUEST)).toBytes()); } - InputStream inputStream = extensionSocket.getInputStream(); - DataInputStream dIn = new DataInputStream(inputStream); + final DataInputStream dIn = new DataInputStream(extensionSocket.getInputStream()); + // listen to incoming data from client while (!extensionSocket.isClosed()) { - int length = dIn.readInt(); - byte[] headerandbody = new byte[length + 4]; + final int length = dIn.readInt(); - int amountRead = 0; - while (amountRead < length) { - amountRead += dIn.read(headerandbody, 4 + amountRead, Math.min(dIn.available(), length - amountRead)); - } + if (length > MAX_PACKET_SIZE) + break; - HPacket packet = new HPacket(headerandbody); - packet.fixLength(); + final HPacket packet = readPacket(dIn, length); if (packet.headerId() == NetworkExtensionInfo.INCOMING_MESSAGES_IDS.EXTENSIONINFO) { - NetworkExtension gEarthExtension = new NetworkExtension( - packet, - extensionSocket - ); - if (Authenticator.evaluate(gEarthExtension)) { + final NetworkExtension gEarthExtension = new NetworkExtension(packet, extensionSocket); + + if (Authenticator.evaluate(gEarthExtension)) observer.onExtensionProduced(gEarthExtension); - } - else { + else gEarthExtension.close(); - } break; } } - - } catch (IOException ignored) {} + } catch (IOException ignored) { + } }).start(); } - } catch (IOException e) {e.printStackTrace();} + } catch (IOException e) { + e.printStackTrace(); + } }).start(); } @@ -94,7 +134,24 @@ public class NetworkExtensionsProducer implements ExtensionProducer { } } + private HPacket readPacket(DataInputStream dIn, int length) throws IOException { + final byte[] headerAndBody = new byte[length + 4]; + + int amountRead = 0; + while (amountRead < length) + amountRead += dIn.read(headerAndBody, 4 + amountRead, Math.min(dIn.available(), length - amountRead)); + + final HPacket packet = new HPacket(headerAndBody); + packet.fixLength(); + return packet; + } + + /** + * Retrieves the {@link ServerSocket#getLocalPort()} of {@link #serverSocket}. + * + * @return the port number to which {@link #serverSocket} is listening or -1 if the socket is not bound yet. + */ public int getPort() { return serverSocket.getLocalPort(); } -} +} \ No newline at end of file