Introduce 1.0.1-BETA packet model with 1.0.0-BETA backwards compatibility
This commit is contained in:
6
pom.xml
6
pom.xml
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<groupId>org.openautonomousconnection</groupId>
|
<groupId>org.openautonomousconnection</groupId>
|
||||||
<artifactId>Protocol</artifactId>
|
<artifactId>Protocol</artifactId>
|
||||||
<version>1.0.0-BETA.1.1</version>
|
<version>1.0.1-BETA.0.1</version>
|
||||||
<organization>
|
<organization>
|
||||||
<name>Open Autonomous Connection</name>
|
<name>Open Autonomous Connection</name>
|
||||||
<url>https://open-autonomous-connection.org/</url>
|
<url>https://open-autonomous-connection.org/</url>
|
||||||
@@ -84,12 +84,12 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>dev.unlegitdqrk</groupId>
|
<groupId>dev.unlegitdqrk</groupId>
|
||||||
<artifactId>unlegitlibrary</artifactId>
|
<artifactId>unlegitlibrary</artifactId>
|
||||||
<version>1.8.1</version>
|
<version>1.8.2</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.projectlombok</groupId>
|
<groupId>org.projectlombok</groupId>
|
||||||
<artifactId>lombok</artifactId>
|
<artifactId>lombok</artifactId>
|
||||||
<version>1.18.38</version>
|
<version>1.18.42</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
package org.openautonomousconnection.protocol;
|
package org.openautonomousconnection.protocol;
|
||||||
|
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.addon.AddonLoader;
|
||||||
import dev.unlegitdqrk.unlegitlibrary.file.FileUtils;
|
import dev.unlegitdqrk.unlegitlibrary.file.FileUtils;
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.ClientAuthMode;
|
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.ClientAuthMode;
|
||||||
import dev.unlegitdqrk.unlegitlibrary.utils.Logger;
|
import dev.unlegitdqrk.unlegitlibrary.utils.Logger;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
|
||||||
import org.openautonomousconnection.protocol.annotations.ProtocolInfo;
|
import org.openautonomousconnection.protocol.annotations.ProtocolInfo;
|
||||||
import org.openautonomousconnection.protocol.listeners.ClientListener;
|
import org.openautonomousconnection.protocol.listeners.ClientListener;
|
||||||
import org.openautonomousconnection.protocol.listeners.CustomServerListener;
|
import org.openautonomousconnection.protocol.listeners.CustomServerListener;
|
||||||
@@ -14,18 +14,28 @@ import org.openautonomousconnection.protocol.packets.v1_0_0.beta.INSQueryPacket;
|
|||||||
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.INSResponsePacket;
|
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.INSResponsePacket;
|
||||||
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.WebRequestPacket;
|
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.WebRequestPacket;
|
||||||
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.WebResponsePacket;
|
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.WebResponsePacket;
|
||||||
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.stream.WebStreamChunkPacket;
|
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.stream.WebStreamChunkPacket_v1_0_0_B;
|
||||||
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.stream.WebStreamEndPacket;
|
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.stream.WebStreamEndPacket_v1_0_0_B;
|
||||||
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.stream.WebStreamStartPacket;
|
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.stream.WebStreamStartPacket_v1_0_0_B;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.document.WebDocumentApplyRequestPacket;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.document.WebDocumentApplyResponsePacket;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.document.WebDocumentSnapshotEventPacket;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.navigate.WebNavigateAckPacket;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.navigate.WebNavigateRequestPacket;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.resource.WebResourceRequestPacket;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.resource.WebResourceResponsePacket;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.stream.WebStreamChunkPacket_v1_0_1_B;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.stream.WebStreamEndPacket_v1_0_1_B;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.stream.WebStreamStartPacket_v1_0_1_B;
|
||||||
import org.openautonomousconnection.protocol.side.client.ProtocolClient;
|
import org.openautonomousconnection.protocol.side.client.ProtocolClient;
|
||||||
import org.openautonomousconnection.protocol.side.ins.ProtocolINSServer;
|
import org.openautonomousconnection.protocol.side.ins.ProtocolINSServer;
|
||||||
import org.openautonomousconnection.protocol.side.server.ProtocolCustomServer;
|
import org.openautonomousconnection.protocol.side.server.ProtocolCustomServer;
|
||||||
import org.openautonomousconnection.protocol.side.web.ProtocolWebServer;
|
|
||||||
import org.openautonomousconnection.protocol.versions.ProtocolVersion;
|
import org.openautonomousconnection.protocol.versions.ProtocolVersion;
|
||||||
|
import org.openautonomousconnection.protocol.versions.v1_0_0.beta.ProtocolWebServer_1_0_0_B;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.Proxy;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The main bridge class for the protocol connection.
|
* The main bridge class for the protocol connection.
|
||||||
@@ -63,12 +73,8 @@ public final class ProtocolBridge {
|
|||||||
@Getter
|
@Getter
|
||||||
private ProtocolCustomServer protocolServer;
|
private ProtocolCustomServer protocolServer;
|
||||||
|
|
||||||
/**
|
|
||||||
* The proxy for client side
|
|
||||||
*/
|
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
private AddonLoader addonLoader;
|
||||||
private Proxy proxy;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the ProtocolBridge instance for the client side
|
* Initialize the ProtocolBridge instance for the client side
|
||||||
@@ -76,24 +82,25 @@ public final class ProtocolBridge {
|
|||||||
* @param protocolServer The ProtocolCustomServer instance
|
* @param protocolServer The ProtocolCustomServer instance
|
||||||
* @param protocolValues The ProtocolSettings instance
|
* @param protocolValues The ProtocolSettings instance
|
||||||
* @param protocolVersion The ProtocolVersion instance
|
* @param protocolVersion The ProtocolVersion instance
|
||||||
* @param logFolder The folder to store the log files
|
* @param logger The logger
|
||||||
|
* @param addonLoader The Addon loader to load custom extensions
|
||||||
* @throws Exception if an error occurs while initializing the ProtocolBridge
|
* @throws Exception if an error occurs while initializing the ProtocolBridge
|
||||||
*/
|
*/
|
||||||
public ProtocolBridge(ProtocolCustomServer protocolServer, ProtocolValues protocolValues, ProtocolVersion protocolVersion, File logFolder) throws Exception {
|
public ProtocolBridge(ProtocolCustomServer protocolServer, ProtocolValues protocolValues, ProtocolVersion protocolVersion,
|
||||||
|
Logger logger, AddonLoader addonLoader) throws Exception {
|
||||||
// Assign the parameters to the class fields
|
// Assign the parameters to the class fields
|
||||||
this.protocolServer = protocolServer;
|
this.protocolServer = protocolServer;
|
||||||
this.protocolValues = protocolValues;
|
this.protocolValues = protocolValues;
|
||||||
this.protocolVersion = protocolVersion;
|
this.protocolVersion = protocolVersion;
|
||||||
|
this.logger = logger;
|
||||||
|
this.addonLoader = addonLoader;
|
||||||
|
|
||||||
if (protocolServer instanceof ProtocolINSServer) {
|
if (protocolServer instanceof ProtocolINSServer)
|
||||||
protocolServer.attachBridge(this, null, false, ClientAuthMode.NONE);
|
protocolServer.attachBridge(this, null, false, ClientAuthMode.NONE);
|
||||||
} else
|
else
|
||||||
protocolServer.attachBridge(this, protocolValues.keyPass, protocolValues.ssl, protocolValues.authMode);
|
protocolServer.attachBridge(this, protocolValues.keyPass, protocolValues.ssl, protocolValues.authMode);
|
||||||
|
|
||||||
// Initialize the logger and protocol version
|
|
||||||
initializeLogger(logFolder);
|
|
||||||
initializeProtocolVersion();
|
initializeProtocolVersion();
|
||||||
|
|
||||||
downloadLicenses();
|
downloadLicenses();
|
||||||
|
|
||||||
// Register the appropriate listeners and packets
|
// Register the appropriate listeners and packets
|
||||||
@@ -107,23 +114,24 @@ public final class ProtocolBridge {
|
|||||||
* @param protocolClient The ProtocolClient instance
|
* @param protocolClient The ProtocolClient instance
|
||||||
* @param protocolValues The ProtocolSettings instance
|
* @param protocolValues The ProtocolSettings instance
|
||||||
* @param protocolVersion The ProtocolVersion instance
|
* @param protocolVersion The ProtocolVersion instance
|
||||||
* @param logFolder The folder to store the log files
|
* @param logger The logger
|
||||||
|
* @param addonLoader The Addon loader to load custom extensions
|
||||||
* @throws Exception if an error occurs while initializing the ProtocolBridge
|
* @throws Exception if an error occurs while initializing the ProtocolBridge
|
||||||
*/
|
*/
|
||||||
@ProtocolInfo(protocolSide = ProtocolVersion.ProtocolSide.CLIENT)
|
@ProtocolInfo(protocolSide = ProtocolVersion.ProtocolSide.CLIENT)
|
||||||
public ProtocolBridge(ProtocolClient protocolClient, ProtocolValues protocolValues, ProtocolVersion protocolVersion, File logFolder) throws Exception {
|
public ProtocolBridge(ProtocolClient protocolClient, ProtocolValues protocolValues, ProtocolVersion protocolVersion,
|
||||||
|
Logger logger, AddonLoader addonLoader) throws Exception {
|
||||||
// Assign the parameters to the class fields
|
// Assign the parameters to the class fields
|
||||||
this.protocolClient = protocolClient;
|
this.protocolClient = protocolClient;
|
||||||
this.protocolValues = protocolValues;
|
this.protocolValues = protocolValues;
|
||||||
this.protocolVersion = protocolVersion;
|
this.protocolVersion = protocolVersion;
|
||||||
|
this.logger = logger;
|
||||||
|
this.addonLoader = addonLoader;
|
||||||
|
|
||||||
protocolClient.attachBridge(this);
|
protocolClient.attachBridge(this);
|
||||||
|
initializeProtocolVersion();
|
||||||
downloadLicenses();
|
downloadLicenses();
|
||||||
|
|
||||||
// Initialize the logger and protocol version
|
|
||||||
initializeLogger(logFolder);
|
|
||||||
initializeProtocolVersion();
|
|
||||||
|
|
||||||
// Register the appropriate listeners and packets
|
// Register the appropriate listeners and packets
|
||||||
registerListeners();
|
registerListeners();
|
||||||
@@ -132,8 +140,10 @@ public final class ProtocolBridge {
|
|||||||
|
|
||||||
private void downloadLicenses() throws IOException {
|
private void downloadLicenses() throws IOException {
|
||||||
File licensesFolder = new File("licenses");
|
File licensesFolder = new File("licenses");
|
||||||
|
|
||||||
if (!licensesFolder.exists() || !licensesFolder.isDirectory()) {
|
if (!licensesFolder.exists() || !licensesFolder.isDirectory()) {
|
||||||
if (licensesFolder.exists()) licensesFolder.delete();
|
if (licensesFolder.exists()) licensesFolder.delete();
|
||||||
|
|
||||||
File output = new File("licenses.zip");
|
File output = new File("licenses.zip");
|
||||||
output.createNewFile();
|
output.createNewFile();
|
||||||
FileUtils.downloadFile("https://open-autonomous-connection.org/assets/licenses.zip", output);
|
FileUtils.downloadFile("https://open-autonomous-connection.org/assets/licenses.zip", output);
|
||||||
@@ -143,27 +153,63 @@ public final class ProtocolBridge {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register the appropriate packets based on the current protocol version
|
* Register the appropriate packets
|
||||||
|
*
|
||||||
|
* <p>Overview of all Packets
|
||||||
|
*
|
||||||
|
* <table border="2">
|
||||||
|
* <tr><th>ID</th><th>Packet</th><th>ProtocolVersion</th></tr>
|
||||||
|
*
|
||||||
|
* <tr><td>8</td><td>{@link org.openautonomousconnection.protocol.packets.v1_0_0.beta.AuthPacket AuthPacket}</td><td>{@link org.openautonomousconnection.protocol.versions.ProtocolVersion#PV_1_0_0_BETA PV_1_0_0_BETA}</td></tr>
|
||||||
|
* <tr><td>7</td><td>{@link org.openautonomousconnection.protocol.packets.v1_0_0.beta.INSQueryPacket INSQueryPacket}</td><td>{@link org.openautonomousconnection.protocol.versions.ProtocolVersion#PV_1_0_0_BETA PV_1_0_0_BETA}</td></tr>
|
||||||
|
* <tr><td>6</td><td>{@link org.openautonomousconnection.protocol.packets.v1_0_0.beta.INSResponsePacket INSResponsePacket}</td><td>{@link org.openautonomousconnection.protocol.versions.ProtocolVersion#PV_1_0_0_BETA PV_1_0_0_BETA}</td></tr>
|
||||||
|
* <tr><td>10</td><td>{@link org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.WebRequestPacket WebRequestPacket}</td><td>{@link org.openautonomousconnection.protocol.versions.ProtocolVersion#PV_1_0_0_BETA PV_1_0_0_BETA}</td></tr>
|
||||||
|
* <tr><td>9</td><td>{@link org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.WebResponsePacket WebResponsePacket}</td><td>{@link org.openautonomousconnection.protocol.versions.ProtocolVersion#PV_1_0_0_BETA PV_1_0_0_BETA}</td></tr>
|
||||||
|
* <tr><td>11</td><td>{@link WebStreamStartPacket_v1_0_0_B v1_0_0.beta.web.stream.WebStreamStartPacket}</td><td>{@link org.openautonomousconnection.protocol.versions.ProtocolVersion#PV_1_0_0_BETA PV_1_0_0_BETA}</td></tr>
|
||||||
|
* <tr><td>13</td><td>{@link WebStreamChunkPacket_v1_0_0_B v1_0_0.beta.web.stream.WebStreamChunkPacket}</td><td>{@link org.openautonomousconnection.protocol.versions.ProtocolVersion#PV_1_0_0_BETA PV_1_0_0_BETA}</td></tr>
|
||||||
|
* <tr><td>12</td><td>{@link WebStreamEndPacket_v1_0_0_B v1_0_0.beta.web.stream.WebStreamEndPacket}</td><td>{@link org.openautonomousconnection.protocol.versions.ProtocolVersion#PV_1_0_0_BETA PV_1_0_0_BETA}</td></tr>
|
||||||
|
*
|
||||||
|
* <tr><td>1</td><td>{@link org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.navigate.WebNavigateRequestPacket WebNavigateRequestPacket}</td><td>{@link org.openautonomousconnection.protocol.versions.ProtocolVersion#PV_1_0_1_BETA PV_1_0_1_BETA}</td></tr>
|
||||||
|
* <tr><td>2</td><td>{@link org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.navigate.WebNavigateAckPacket WebNavigateAckPacket}</td><td>{@link org.openautonomousconnection.protocol.versions.ProtocolVersion#PV_1_0_1_BETA PV_1_0_1_BETA}</td></tr>
|
||||||
|
* <tr><td>3</td><td>{@link org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.resource.WebResourceRequestPacket WebResourceRequestPacket}</td><td>{@link org.openautonomousconnection.protocol.versions.ProtocolVersion#PV_1_0_1_BETA PV_1_0_1_BETA}</td></tr>
|
||||||
|
* <tr><td>4</td><td>{@link org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.resource.WebResourceResponsePacket WebResourceResponsePacket}</td><td>{@link org.openautonomousconnection.protocol.versions.ProtocolVersion#PV_1_0_1_BETA PV_1_0_1_BETA}</td></tr>
|
||||||
|
* <tr><td>14</td><td>{@link WebStreamStartPacket_v1_0_1_B v1_0_1.beta.web.impl.stream.WebStreamStartPacket}</td><td>{@link org.openautonomousconnection.protocol.versions.ProtocolVersion#PV_1_0_1_BETA PV_1_0_1_BETA}</td></tr>
|
||||||
|
* <tr><td>15</td><td>{@link WebStreamChunkPacket_v1_0_1_B v1_0_1.beta.web.impl.stream.WebStreamChunkPacket}</td><td>{@link org.openautonomousconnection.protocol.versions.ProtocolVersion#PV_1_0_1_BETA PV_1_0_1_BETA}</td></tr>
|
||||||
|
* <tr><td>16</td><td>{@link WebStreamEndPacket_v1_0_1_B v1_0_1.beta.web.impl.stream.WebStreamEndPacket}</td><td>{@link org.openautonomousconnection.protocol.versions.ProtocolVersion#PV_1_0_1_BETA PV_1_0_1_BETA}</td></tr>
|
||||||
|
* <tr><td>17</td><td>{@link org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.document.WebDocumentSnapshotEventPacket WebDocumentSnapshotEventPacket}</td><td>{@link org.openautonomousconnection.protocol.versions.ProtocolVersion#PV_1_0_1_BETA PV_1_0_1_BETA}</td></tr>
|
||||||
|
* <tr><td>5</td><td>{@link org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.document.WebDocumentApplyResponsePacket WebDocumentApplyResponsePacket}</td><td>{@link org.openautonomousconnection.protocol.versions.ProtocolVersion#PV_1_0_1_BETA PV_1_0_1_BETA}</td></tr>
|
||||||
|
* <tr><td>18</td><td>{@link org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.document.WebDocumentApplyRequestPacket WebDocumentApplyRequestPacket}</td><td>{@link org.openautonomousconnection.protocol.versions.ProtocolVersion#PV_1_0_1_BETA PV_1_0_1_BETA}</td></tr>
|
||||||
|
* </table>
|
||||||
|
*
|
||||||
|
* @see org.openautonomousconnection.protocol.versions.ProtocolVersion
|
||||||
*/
|
*/
|
||||||
private void registerPackets() {
|
private void registerPackets() {
|
||||||
// 1.0.0-BETA packets
|
// 1.0.0-BETA packets
|
||||||
if (isPacketSupported(new AuthPacket(this)))
|
registerPacket(AuthPacket::new);
|
||||||
protocolValues.packetHandler.registerPacket(() -> new AuthPacket(this));
|
registerPacket(INSQueryPacket::new);
|
||||||
if (isPacketSupported(new INSQueryPacket())) protocolValues.packetHandler.registerPacket(INSQueryPacket::new);
|
registerPacket(() -> new INSResponsePacket(this));
|
||||||
if (isPacketSupported(new INSResponsePacket(this)))
|
registerPacket(WebRequestPacket::new);
|
||||||
protocolValues.packetHandler.registerPacket(() -> new INSResponsePacket(this));
|
registerPacket(WebResponsePacket::new);
|
||||||
if (isPacketSupported(new WebRequestPacket()))
|
registerPacket(WebStreamStartPacket_v1_0_0_B::new);
|
||||||
protocolValues.packetHandler.registerPacket(WebRequestPacket::new);
|
registerPacket(WebStreamChunkPacket_v1_0_0_B::new);
|
||||||
if (isPacketSupported(new WebResponsePacket()))
|
registerPacket(WebStreamEndPacket_v1_0_0_B::new);
|
||||||
protocolValues.packetHandler.registerPacket(WebResponsePacket::new);
|
|
||||||
if (isPacketSupported(new WebStreamChunkPacket()))
|
// 1.0.1-BETA Packets
|
||||||
protocolValues.packetHandler.registerPacket(WebStreamChunkPacket::new);
|
registerPacket(WebDocumentApplyRequestPacket::new);
|
||||||
if (isPacketSupported(new WebStreamStartPacket()))
|
registerPacket(WebDocumentApplyResponsePacket::new);
|
||||||
protocolValues.packetHandler.registerPacket(WebStreamStartPacket::new);
|
registerPacket(WebDocumentSnapshotEventPacket::new);
|
||||||
if (isPacketSupported(new WebStreamEndPacket()))
|
registerPacket(WebNavigateRequestPacket::new);
|
||||||
protocolValues.packetHandler.registerPacket(WebStreamEndPacket::new);
|
registerPacket(WebNavigateAckPacket::new);
|
||||||
|
registerPacket(WebResourceRequestPacket::new);
|
||||||
|
registerPacket(WebResourceResponsePacket::new);
|
||||||
|
registerPacket(WebStreamStartPacket_v1_0_1_B::new);
|
||||||
|
registerPacket(WebStreamChunkPacket_v1_0_1_B::new);
|
||||||
|
registerPacket(WebStreamEndPacket_v1_0_1_B::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void registerPacket(Supplier<? extends OACPacket> factory) {
|
||||||
|
if (isPacketSupported(factory.get())) protocolValues.packetHandler.registerPacket(factory);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register the appropriate listeners based on the current side
|
* Register the appropriate listeners based on the current side
|
||||||
@@ -184,27 +230,6 @@ public final class ProtocolBridge {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize the logger instance
|
|
||||||
*
|
|
||||||
* @param logFolder The folder to store the log files
|
|
||||||
*/
|
|
||||||
private void initializeLogger(File logFolder) {
|
|
||||||
// Create a temporary logger instance to avoid final field issues
|
|
||||||
Logger tmpLogger = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Initialize temporary logger
|
|
||||||
tmpLogger = new Logger(logFolder, false, true);
|
|
||||||
} catch (IOException | NoSuchFieldException | IllegalAccessException exception) {
|
|
||||||
exception.printStackTrace();
|
|
||||||
System.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assign the temporary logger to the final field
|
|
||||||
this.logger = tmpLogger;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the protocol version
|
* Initialize the protocol version
|
||||||
* Validate if the protocol version is valid for the current side
|
* Validate if the protocol version is valid for the current side
|
||||||
@@ -244,6 +269,7 @@ public final class ProtocolBridge {
|
|||||||
*/
|
*/
|
||||||
public boolean isProtocolSupported(ProtocolVersion.Protocol protocol) {
|
public boolean isProtocolSupported(ProtocolVersion.Protocol protocol) {
|
||||||
boolean yes = false;
|
boolean yes = false;
|
||||||
|
|
||||||
for (ProtocolVersion compatibleVersion : protocolVersion.getCompatibleVersions()) {
|
for (ProtocolVersion compatibleVersion : protocolVersion.getCompatibleVersions()) {
|
||||||
// Check if the compatible version supports the target protocol
|
// Check if the compatible version supports the target protocol
|
||||||
yes = compatibleVersion.getSupportedProtocols().contains(protocol);
|
yes = compatibleVersion.getSupportedProtocols().contains(protocol);
|
||||||
@@ -324,7 +350,7 @@ public final class ProtocolBridge {
|
|||||||
* @return true if the current instance is running as a web server, false otherwise
|
* @return true if the current instance is running as a web server, false otherwise
|
||||||
*/
|
*/
|
||||||
public boolean isRunningAsWebServer() {
|
public boolean isRunningAsWebServer() {
|
||||||
return isRunningAsServer() && protocolServer instanceof ProtocolWebServer;
|
return isRunningAsServer() && protocolServer instanceof ProtocolWebServer_1_0_0_B;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.openautonomousconnection.protocol.listeners;
|
package org.openautonomousconnection.protocol.listeners;
|
||||||
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.event.EventListener;
|
import dev.unlegitdqrk.unlegitlibrary.event.EventListener;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.event.EventPriority;
|
||||||
import dev.unlegitdqrk.unlegitlibrary.event.Listener;
|
import dev.unlegitdqrk.unlegitlibrary.event.Listener;
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.client.events.state.ClientConnectedEvent;
|
import dev.unlegitdqrk.unlegitlibrary.network.system.client.events.state.ClientConnectedEvent;
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.TransportProtocol;
|
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.TransportProtocol;
|
||||||
@@ -37,7 +38,7 @@ public final class ClientListener extends EventListener {
|
|||||||
*
|
*
|
||||||
* @param event The client connected event.
|
* @param event The client connected event.
|
||||||
*/
|
*/
|
||||||
@Listener
|
@Listener(priority = EventPriority.HIGHEST)
|
||||||
public void onConnect(ClientConnectedEvent event) {
|
public void onConnect(ClientConnectedEvent event) {
|
||||||
try {
|
try {
|
||||||
event.getClient().sendPacket(new AuthPacket(client.getProtocolBridge()), TransportProtocol.TCP);
|
event.getClient().sendPacket(new AuthPacket(client.getProtocolBridge()), TransportProtocol.TCP);
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.WebRequestP
|
|||||||
import org.openautonomousconnection.protocol.side.ins.ProtocolINSServer;
|
import org.openautonomousconnection.protocol.side.ins.ProtocolINSServer;
|
||||||
import org.openautonomousconnection.protocol.side.server.CustomConnectedClient;
|
import org.openautonomousconnection.protocol.side.server.CustomConnectedClient;
|
||||||
import org.openautonomousconnection.protocol.side.server.ProtocolCustomServer;
|
import org.openautonomousconnection.protocol.side.server.ProtocolCustomServer;
|
||||||
import org.openautonomousconnection.protocol.side.web.ProtocolWebServer;
|
|
||||||
import org.openautonomousconnection.protocol.versions.v1_0_0.beta.INSRecord;
|
import org.openautonomousconnection.protocol.versions.v1_0_0.beta.INSRecord;
|
||||||
import org.openautonomousconnection.protocol.versions.v1_0_0.beta.INSResponseStatus;
|
import org.openautonomousconnection.protocol.versions.v1_0_0.beta.INSResponseStatus;
|
||||||
|
import org.openautonomousconnection.protocol.versions.v1_0_0.beta.ProtocolWebServer_1_0_0_B;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -47,7 +47,7 @@ public final class CustomServerListener extends EventListener {
|
|||||||
*
|
*
|
||||||
* @param event The connection handler connected event.
|
* @param event The connection handler connected event.
|
||||||
*/
|
*/
|
||||||
@Listener
|
@Listener(priority = EventPriority.HIGHEST)
|
||||||
public void onConnect(S_ClientConnectedEvent event) {
|
public void onConnect(S_ClientConnectedEvent event) {
|
||||||
try {
|
try {
|
||||||
server.getClients().add(new CustomConnectedClient(event.getClient(), server));
|
server.getClients().add(new CustomConnectedClient(event.getClient(), server));
|
||||||
@@ -57,13 +57,13 @@ public final class CustomServerListener extends EventListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Listener(priority = EventPriority.HIGH)
|
@Listener(priority = EventPriority.HIGHEST)
|
||||||
public void onPacketWeb(S_PacketReadEvent event) {
|
public void onPacketWeb(S_PacketReadEvent event) {
|
||||||
if (!server.getProtocolBridge().isRunningAsWebServer()) return;
|
if (!server.getProtocolBridge().isRunningAsWebServer()) return;
|
||||||
if (event.getPacket() instanceof WebRequestPacket) {
|
if (event.getPacket() instanceof WebRequestPacket) {
|
||||||
try {
|
try {
|
||||||
event.getClient().sendPacket(
|
event.getClient().sendPacket(
|
||||||
((ProtocolWebServer) server.getProtocolBridge().getProtocolServer()).
|
((ProtocolWebServer_1_0_0_B) server.getProtocolBridge().getProtocolServer()).
|
||||||
onWebRequest(server.getClientByID(event.getClient().getUniqueID()), (WebRequestPacket) event.getPacket()),
|
onWebRequest(server.getClientByID(event.getClient().getUniqueID()), (WebRequestPacket) event.getPacket()),
|
||||||
TransportProtocol.TCP);
|
TransportProtocol.TCP);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
@@ -88,7 +88,7 @@ public final class CustomServerListener extends EventListener {
|
|||||||
*
|
*
|
||||||
* @param event The packet event received by the network system.
|
* @param event The packet event received by the network system.
|
||||||
*/
|
*/
|
||||||
@Listener
|
@Listener(priority = EventPriority.HIGHEST)
|
||||||
public void onPacketINS(S_PacketReadEvent event) {
|
public void onPacketINS(S_PacketReadEvent event) {
|
||||||
if (!(event.getPacket() instanceof INSQueryPacket q)) return;
|
if (!(event.getPacket() instanceof INSQueryPacket q)) return;
|
||||||
if (!server.getProtocolBridge().isRunningAsINSServer()) return;
|
if (!server.getProtocolBridge().isRunningAsINSServer()) return;
|
||||||
|
|||||||
@@ -55,8 +55,6 @@ public final class AuthPacket extends OACPacket {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onWrite(DataOutputStream objectOutputStream) throws IOException {
|
public void onWrite(DataOutputStream objectOutputStream) throws IOException {
|
||||||
|
|
||||||
|
|
||||||
if (protocolBridge.isRunningAsINSServer()) {
|
if (protocolBridge.isRunningAsINSServer()) {
|
||||||
objectOutputStream.writeBoolean(true);
|
objectOutputStream.writeBoolean(true);
|
||||||
objectOutputStream.writeUTF(protocolBridge.getProtocolVersion().name());
|
objectOutputStream.writeUTF(protocolBridge.getProtocolVersion().name());
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ public final class INSQueryPacket extends OACPacket {
|
|||||||
* @param clientId Sender client ID for routing.
|
* @param clientId Sender client ID for routing.
|
||||||
*/
|
*/
|
||||||
public INSQueryPacket(String tln, String name, String sub, INSRecordType type, UUID clientId) {
|
public INSQueryPacket(String tln, String name, String sub, INSRecordType type, UUID clientId) {
|
||||||
super(7, ProtocolVersion.PV_1_0_0_BETA);
|
this();
|
||||||
this.TLN = tln;
|
this.TLN = tln;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.sub = sub;
|
this.sub = sub;
|
||||||
|
|||||||
@@ -46,11 +46,10 @@ public final class INSResponsePacket extends OACPacket {
|
|||||||
* @param bridge Protocol runtime context.
|
* @param bridge Protocol runtime context.
|
||||||
*/
|
*/
|
||||||
public INSResponsePacket(INSResponseStatus status, List<INSRecord> records, UUID clientId, ProtocolBridge bridge) {
|
public INSResponsePacket(INSResponseStatus status, List<INSRecord> records, UUID clientId, ProtocolBridge bridge) {
|
||||||
super(6, ProtocolVersion.PV_1_0_0_BETA);
|
this(bridge);
|
||||||
this.status = status;
|
this.status = status;
|
||||||
this.records = records;
|
this.records = records;
|
||||||
this.clientId = clientId;
|
this.clientId = clientId;
|
||||||
this.bridge = bridge;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ public final class WebRequestPacket extends OACPacket {
|
|||||||
* @param body request body (may be null)
|
* @param body request body (may be null)
|
||||||
*/
|
*/
|
||||||
public WebRequestPacket(String path, WebRequestMethod method, Map<String, String> headers, byte[] body) {
|
public WebRequestPacket(String path, WebRequestMethod method, Map<String, String> headers, byte[] body) {
|
||||||
super(10, ProtocolVersion.PV_1_0_0_BETA);
|
this();
|
||||||
this.path = (path != null) ? path : "/";
|
this.path = (path != null) ? path : "/";
|
||||||
this.method = (method != null) ? method : WebRequestMethod.GET;
|
this.method = (method != null) ? method : WebRequestMethod.GET;
|
||||||
this.headers = (headers != null) ? new LinkedHashMap<>(headers) : Collections.emptyMap();
|
this.headers = (headers != null) ? new LinkedHashMap<>(headers) : Collections.emptyMap();
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ public final class WebResponsePacket extends OACPacket {
|
|||||||
* @param body response body (may be null)
|
* @param body response body (may be null)
|
||||||
*/
|
*/
|
||||||
public WebResponsePacket(int statusCode, String contentType, Map<String, String> headers, byte[] body) {
|
public WebResponsePacket(int statusCode, String contentType, Map<String, String> headers, byte[] body) {
|
||||||
super(9, ProtocolVersion.PV_1_0_0_BETA);
|
this();
|
||||||
this.statusCode = statusCode;
|
this.statusCode = statusCode;
|
||||||
this.contentType = (contentType != null) ? contentType : "text/plain";
|
this.contentType = (contentType != null) ? contentType : "text/plain";
|
||||||
this.headers = (headers != null) ? new LinkedHashMap<>(headers) : Collections.emptyMap();
|
this.headers = (headers != null) ? new LinkedHashMap<>(headers) : Collections.emptyMap();
|
||||||
|
|||||||
@@ -9,19 +9,19 @@ import java.io.DataOutputStream;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public final class WebStreamChunkPacket extends OACPacket {
|
public final class WebStreamChunkPacket_v1_0_0_B extends OACPacket {
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private int seq;
|
private int seq;
|
||||||
@Getter
|
@Getter
|
||||||
private byte[] data;
|
private byte[] data;
|
||||||
|
|
||||||
public WebStreamChunkPacket() {
|
public WebStreamChunkPacket_v1_0_0_B() {
|
||||||
super(13, ProtocolVersion.PV_1_0_0_BETA);
|
super(13, ProtocolVersion.PV_1_0_0_BETA);
|
||||||
}
|
}
|
||||||
|
|
||||||
public WebStreamChunkPacket(int seq, byte[] data) {
|
public WebStreamChunkPacket_v1_0_0_B(int seq, byte[] data) {
|
||||||
super(13, ProtocolVersion.PV_1_0_0_BETA);
|
this();
|
||||||
this.seq = seq;
|
this.seq = seq;
|
||||||
this.data = (data != null) ? data : new byte[0];
|
this.data = (data != null) ? data : new byte[0];
|
||||||
}
|
}
|
||||||
@@ -9,17 +9,17 @@ import java.io.DataOutputStream;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public final class WebStreamEndPacket extends OACPacket {
|
public final class WebStreamEndPacket_v1_0_0_B extends OACPacket {
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private boolean ok;
|
private boolean ok;
|
||||||
|
|
||||||
public WebStreamEndPacket() {
|
public WebStreamEndPacket_v1_0_0_B() {
|
||||||
super(12, ProtocolVersion.PV_1_0_0_BETA);
|
super(13, ProtocolVersion.PV_1_0_0_BETA);
|
||||||
}
|
}
|
||||||
|
|
||||||
public WebStreamEndPacket(boolean ok) {
|
public WebStreamEndPacket_v1_0_0_B(boolean ok) {
|
||||||
super(12, ProtocolVersion.PV_1_0_0_BETA);
|
this();
|
||||||
this.ok = ok;
|
this.ok = ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ import java.util.UUID;
|
|||||||
* <p>Important: This packet encodes headers using a deterministic UTF-based map format
|
* <p>Important: This packet encodes headers using a deterministic UTF-based map format
|
||||||
* (int size, then size times: UTF key, UTF value) instead of Java object serialization.</p>
|
* (int size, then size times: UTF key, UTF value) instead of Java object serialization.</p>
|
||||||
*/
|
*/
|
||||||
public final class WebStreamStartPacket extends OACPacket {
|
public final class WebStreamStartPacket_v1_0_0_B extends OACPacket {
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private int statusCode;
|
private int statusCode;
|
||||||
@@ -35,7 +35,7 @@ public final class WebStreamStartPacket extends OACPacket {
|
|||||||
/**
|
/**
|
||||||
* Creates an empty start packet (used by PacketHandler factory).
|
* Creates an empty start packet (used by PacketHandler factory).
|
||||||
*/
|
*/
|
||||||
public WebStreamStartPacket() {
|
public WebStreamStartPacket_v1_0_0_B() {
|
||||||
super(11, ProtocolVersion.PV_1_0_0_BETA);
|
super(11, ProtocolVersion.PV_1_0_0_BETA);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,8 +47,8 @@ public final class WebStreamStartPacket extends OACPacket {
|
|||||||
* @param headers headers (may be null)
|
* @param headers headers (may be null)
|
||||||
* @param totalLength total length of the stream (may be -1 if unknown)
|
* @param totalLength total length of the stream (may be -1 if unknown)
|
||||||
*/
|
*/
|
||||||
public WebStreamStartPacket(int statusCode, String contentType, Map<String, String> headers, long totalLength) {
|
public WebStreamStartPacket_v1_0_0_B(int statusCode, String contentType, Map<String, String> headers, long totalLength) {
|
||||||
super(11, ProtocolVersion.PV_1_0_0_BETA);
|
this();
|
||||||
this.statusCode = statusCode;
|
this.statusCode = statusCode;
|
||||||
this.contentType = (contentType != null) ? contentType : "application/octet-stream";
|
this.contentType = (contentType != null) ? contentType : "application/octet-stream";
|
||||||
this.headers = (headers != null) ? new LinkedHashMap<>(headers) : Collections.emptyMap();
|
this.headers = (headers != null) ? new LinkedHashMap<>(headers) : Collections.emptyMap();
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
package org.openautonomousconnection.protocol.packets.v1_0_1.beta.web;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.openautonomousconnection.protocol.packets.OACPacket;
|
||||||
|
import org.openautonomousconnection.protocol.versions.ProtocolVersion;
|
||||||
|
import org.openautonomousconnection.protocol.versions.v1_0_1.beta.WebPacketHeader;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for all Web v1.0.1-BETA packets.
|
||||||
|
*
|
||||||
|
* <p>Ensures the {@link WebPacketHeader} is always serialized first.</p>
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
public abstract class WebPacket extends OACPacket {
|
||||||
|
|
||||||
|
private WebPacketHeader header;
|
||||||
|
|
||||||
|
protected WebPacket(int packetId, ProtocolVersion version) {
|
||||||
|
super(packetId, version);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the header before sending.
|
||||||
|
*
|
||||||
|
* @param header header (required)
|
||||||
|
*/
|
||||||
|
public void setHeader(WebPacketHeader header) {
|
||||||
|
this.header = header;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void onWrite(DataOutputStream out) throws IOException {
|
||||||
|
if (header == null) {
|
||||||
|
throw new IOException("WebPacketHeader is required");
|
||||||
|
}
|
||||||
|
header.write(out);
|
||||||
|
writeBody(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void onRead(DataInputStream in, UUID clientID) throws IOException {
|
||||||
|
this.header = WebPacketHeader.read(in);
|
||||||
|
readBody(in, clientID);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes packet-specific payload after the header.
|
||||||
|
*
|
||||||
|
* @param out stream
|
||||||
|
* @throws IOException on IO error
|
||||||
|
*/
|
||||||
|
protected abstract void writeBody(DataOutputStream out) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads packet-specific payload after the header.
|
||||||
|
*
|
||||||
|
* @param in stream
|
||||||
|
* @param clientID sender client id
|
||||||
|
* @throws IOException on IO error
|
||||||
|
*/
|
||||||
|
protected abstract void readBody(DataInputStream in, UUID clientID) throws IOException;
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.document;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.WebPacket;
|
||||||
|
import org.openautonomousconnection.protocol.versions.ProtocolVersion;
|
||||||
|
import org.openautonomousconnection.protocol.versions.v1_0_1.beta.WebPacketHeader;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request: replace the document content with full HTML.
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
public final class WebDocumentApplyRequestPacket extends WebPacket {
|
||||||
|
private String fullHtml;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registration constructor.
|
||||||
|
*/
|
||||||
|
public WebDocumentApplyRequestPacket() {
|
||||||
|
super(18, ProtocolVersion.PV_1_0_1_BETA);
|
||||||
|
}
|
||||||
|
|
||||||
|
public WebDocumentApplyRequestPacket(WebPacketHeader header, String fullHtml) {
|
||||||
|
this();
|
||||||
|
setHeader(header);
|
||||||
|
this.fullHtml = (fullHtml != null) ? fullHtml : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void writeBody(DataOutputStream out) throws IOException {
|
||||||
|
out.writeUTF((fullHtml != null) ? fullHtml : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void readBody(DataInputStream in, UUID clientID) throws IOException {
|
||||||
|
this.fullHtml = in.readUTF();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.document;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.WebPacket;
|
||||||
|
import org.openautonomousconnection.protocol.versions.ProtocolVersion;
|
||||||
|
import org.openautonomousconnection.protocol.versions.v1_0_1.beta.WebPacketHeader;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response: result of applying document changes.
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
public final class WebDocumentApplyResponsePacket extends WebPacket {
|
||||||
|
private boolean ok;
|
||||||
|
private String error;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registration constructor.
|
||||||
|
*/
|
||||||
|
public WebDocumentApplyResponsePacket() {
|
||||||
|
super(5, ProtocolVersion.PV_1_0_1_BETA);
|
||||||
|
}
|
||||||
|
|
||||||
|
public WebDocumentApplyResponsePacket(WebPacketHeader header, boolean ok, String error) {
|
||||||
|
this();
|
||||||
|
setHeader(header);
|
||||||
|
this.ok = ok;
|
||||||
|
this.error = error;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void writeBody(DataOutputStream out) throws IOException {
|
||||||
|
out.writeBoolean(ok);
|
||||||
|
out.writeBoolean(error != null);
|
||||||
|
if (error != null) out.writeUTF(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void readBody(DataInputStream in, UUID clientID) throws IOException {
|
||||||
|
this.ok = in.readBoolean();
|
||||||
|
boolean hasErr = in.readBoolean();
|
||||||
|
this.error = hasErr ? in.readUTF() : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.document;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.WebPacket;
|
||||||
|
import org.openautonomousconnection.protocol.versions.ProtocolVersion;
|
||||||
|
import org.openautonomousconnection.protocol.versions.v1_0_1.beta.WebPacketHeader;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event: sends the current document snapshot (HTML) for a page.
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
public final class WebDocumentSnapshotEventPacket extends WebPacket {
|
||||||
|
private String baseUrl;
|
||||||
|
private String html;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registration constructor.
|
||||||
|
*/
|
||||||
|
public WebDocumentSnapshotEventPacket() {
|
||||||
|
super(17, ProtocolVersion.PV_1_0_1_BETA);
|
||||||
|
}
|
||||||
|
|
||||||
|
public WebDocumentSnapshotEventPacket(WebPacketHeader header, String baseUrl, String html) {
|
||||||
|
this();
|
||||||
|
setHeader(header);
|
||||||
|
this.baseUrl = (baseUrl != null) ? baseUrl : "";
|
||||||
|
this.html = (html != null) ? html : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void writeBody(DataOutputStream out) throws IOException {
|
||||||
|
out.writeUTF((baseUrl != null) ? baseUrl : "");
|
||||||
|
out.writeUTF((html != null) ? html : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void readBody(DataInputStream in, UUID clientID) throws IOException {
|
||||||
|
this.baseUrl = in.readUTF();
|
||||||
|
this.html = in.readUTF();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.navigate;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.WebPacket;
|
||||||
|
import org.openautonomousconnection.protocol.versions.ProtocolVersion;
|
||||||
|
import org.openautonomousconnection.protocol.versions.v1_0_1.beta.WebPacketHeader;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Acknowledges (or rejects) a navigation request.
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
public final class WebNavigateAckPacket extends WebPacket {
|
||||||
|
private boolean accepted;
|
||||||
|
private String reason;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registration constructor.
|
||||||
|
*/
|
||||||
|
public WebNavigateAckPacket() {
|
||||||
|
super(2, ProtocolVersion.PV_1_0_1_BETA);
|
||||||
|
}
|
||||||
|
|
||||||
|
public WebNavigateAckPacket(WebPacketHeader header, boolean accepted, String reason) {
|
||||||
|
this();
|
||||||
|
setHeader(header);
|
||||||
|
this.accepted = accepted;
|
||||||
|
this.reason = reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void writeBody(DataOutputStream out) throws IOException {
|
||||||
|
out.writeBoolean(accepted);
|
||||||
|
out.writeBoolean(reason != null);
|
||||||
|
if (reason != null) out.writeUTF(reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void readBody(DataInputStream in, UUID clientID) throws IOException {
|
||||||
|
this.accepted = in.readBoolean();
|
||||||
|
boolean hasReason = in.readBoolean();
|
||||||
|
this.reason = hasReason ? in.readUTF() : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
package org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.navigate;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.WebPacket;
|
||||||
|
import org.openautonomousconnection.protocol.versions.ProtocolVersion;
|
||||||
|
import org.openautonomousconnection.protocol.versions.v1_0_1.beta.WebCacheMode;
|
||||||
|
import org.openautonomousconnection.protocol.versions.v1_0_1.beta.WebPacketHeader;
|
||||||
|
import org.openautonomousconnection.protocol.versions.v1_0_1.beta.WebTransitionType;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigation request for a tab/page.
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
public final class WebNavigateRequestPacket extends WebPacket {
|
||||||
|
private String url;
|
||||||
|
private String referrer;
|
||||||
|
private WebTransitionType transitionType;
|
||||||
|
private long headerProfileId;
|
||||||
|
private WebCacheMode cacheMode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registration constructor.
|
||||||
|
*/
|
||||||
|
public WebNavigateRequestPacket() {
|
||||||
|
super(1, ProtocolVersion.PV_1_0_1_BETA);
|
||||||
|
}
|
||||||
|
|
||||||
|
public WebNavigateRequestPacket(
|
||||||
|
WebPacketHeader header,
|
||||||
|
String url,
|
||||||
|
String referrer,
|
||||||
|
WebTransitionType transitionType,
|
||||||
|
long headerProfileId,
|
||||||
|
WebCacheMode cacheMode
|
||||||
|
) {
|
||||||
|
this();
|
||||||
|
setHeader(header);
|
||||||
|
this.url = (url != null) ? url : "";
|
||||||
|
this.referrer = referrer;
|
||||||
|
this.transitionType = (transitionType != null) ? transitionType : WebTransitionType.TYPED;
|
||||||
|
this.headerProfileId = headerProfileId;
|
||||||
|
this.cacheMode = (cacheMode != null) ? cacheMode : WebCacheMode.DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void writeBody(DataOutputStream out) throws IOException {
|
||||||
|
out.writeUTF((url != null) ? url : "");
|
||||||
|
out.writeBoolean(referrer != null);
|
||||||
|
if (referrer != null) out.writeUTF(referrer);
|
||||||
|
|
||||||
|
out.writeUTF((transitionType != null) ? transitionType.name() : WebTransitionType.TYPED.name());
|
||||||
|
out.writeLong(headerProfileId);
|
||||||
|
out.writeUTF((cacheMode != null) ? cacheMode.name() : WebCacheMode.DEFAULT.name());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void readBody(DataInputStream in, UUID clientID) throws IOException {
|
||||||
|
this.url = in.readUTF();
|
||||||
|
|
||||||
|
boolean hasRef = in.readBoolean();
|
||||||
|
this.referrer = hasRef ? in.readUTF() : null;
|
||||||
|
|
||||||
|
this.transitionType = WebTransitionType.valueOf(in.readUTF());
|
||||||
|
this.headerProfileId = in.readLong();
|
||||||
|
this.cacheMode = WebCacheMode.valueOf(in.readUTF());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
package org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.resource;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.WebPacket;
|
||||||
|
import org.openautonomousconnection.protocol.versions.ProtocolVersion;
|
||||||
|
import org.openautonomousconnection.protocol.versions.v1_0_1.beta.WebCacheMode;
|
||||||
|
import org.openautonomousconnection.protocol.versions.v1_0_1.beta.WebInitiatorType;
|
||||||
|
import org.openautonomousconnection.protocol.versions.v1_0_1.beta.WebPacketHeader;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resource request
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
public final class WebResourceRequestPacket extends WebPacket {
|
||||||
|
private String url;
|
||||||
|
private String method;
|
||||||
|
private Map<String, String> headers;
|
||||||
|
private byte[] body;
|
||||||
|
private String bodyContentType;
|
||||||
|
private WebInitiatorType initiatorType;
|
||||||
|
private WebCacheMode cacheMode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registration constructor.
|
||||||
|
*/
|
||||||
|
public WebResourceRequestPacket() {
|
||||||
|
super(3, ProtocolVersion.PV_1_0_1_BETA);
|
||||||
|
}
|
||||||
|
|
||||||
|
public WebResourceRequestPacket(
|
||||||
|
WebPacketHeader header,
|
||||||
|
String url,
|
||||||
|
String method,
|
||||||
|
Map<String, String> headers,
|
||||||
|
byte[] body,
|
||||||
|
String bodyContentType,
|
||||||
|
WebInitiatorType initiatorType,
|
||||||
|
WebCacheMode cacheMode
|
||||||
|
) {
|
||||||
|
this();
|
||||||
|
setHeader(header);
|
||||||
|
this.url = (url != null) ? url : "";
|
||||||
|
this.method = (method != null) ? method : "GET";
|
||||||
|
this.headers = (headers != null) ? new LinkedHashMap<>(headers) : Collections.emptyMap();
|
||||||
|
this.body = (body != null) ? body : new byte[0];
|
||||||
|
this.bodyContentType = bodyContentType;
|
||||||
|
this.initiatorType = (initiatorType != null) ? initiatorType : WebInitiatorType.OTHER;
|
||||||
|
this.cacheMode = (cacheMode != null) ? cacheMode : WebCacheMode.DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void writeBody(DataOutputStream out) throws IOException {
|
||||||
|
out.writeUTF((url != null) ? url : "");
|
||||||
|
out.writeUTF((method != null) ? method : "GET");
|
||||||
|
|
||||||
|
writeStringMap(out, headers);
|
||||||
|
|
||||||
|
out.writeBoolean(bodyContentType != null);
|
||||||
|
if (bodyContentType != null) out.writeUTF(bodyContentType);
|
||||||
|
|
||||||
|
out.writeUTF((initiatorType != null) ? initiatorType.name() : WebInitiatorType.OTHER.name());
|
||||||
|
out.writeUTF((cacheMode != null) ? cacheMode.name() : WebCacheMode.DEFAULT.name());
|
||||||
|
|
||||||
|
byte[] b = (body != null) ? body : new byte[0];
|
||||||
|
out.writeInt(b.length);
|
||||||
|
out.write(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void readBody(DataInputStream in, UUID clientID) throws IOException {
|
||||||
|
this.url = in.readUTF();
|
||||||
|
this.method = in.readUTF();
|
||||||
|
|
||||||
|
this.headers = readStringMap(in);
|
||||||
|
|
||||||
|
boolean hasBct = in.readBoolean();
|
||||||
|
this.bodyContentType = hasBct ? in.readUTF() : null;
|
||||||
|
|
||||||
|
this.initiatorType = WebInitiatorType.valueOf(in.readUTF());
|
||||||
|
this.cacheMode = WebCacheMode.valueOf(in.readUTF());
|
||||||
|
|
||||||
|
int len = in.readInt();
|
||||||
|
if (len < 0) throw new IOException("Negative body length in WebResourceRequestPacket");
|
||||||
|
this.body = in.readNBytes(len);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
package org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.resource;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.WebPacket;
|
||||||
|
import org.openautonomousconnection.protocol.versions.ProtocolVersion;
|
||||||
|
import org.openautonomousconnection.protocol.versions.v1_0_1.beta.WebPacketHeader;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resource response
|
||||||
|
*
|
||||||
|
* <p>If the response body is streamed, send this packet with an empty body and set STREAM flag in the header.</p>
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
public final class WebResourceResponsePacket extends WebPacket {
|
||||||
|
|
||||||
|
private int statusCode;
|
||||||
|
private String contentType;
|
||||||
|
private Map<String, String> headers;
|
||||||
|
private byte[] body;
|
||||||
|
private String finalUrl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registration constructor.
|
||||||
|
*/
|
||||||
|
public WebResourceResponsePacket() {
|
||||||
|
super(4, ProtocolVersion.PV_1_0_1_BETA);
|
||||||
|
}
|
||||||
|
|
||||||
|
public WebResourceResponsePacket(
|
||||||
|
WebPacketHeader header,
|
||||||
|
int statusCode,
|
||||||
|
String contentType,
|
||||||
|
Map<String, String> headers,
|
||||||
|
byte[] body,
|
||||||
|
String finalUrl
|
||||||
|
) {
|
||||||
|
this();
|
||||||
|
setHeader(header);
|
||||||
|
this.statusCode = statusCode;
|
||||||
|
this.contentType = (contentType != null) ? contentType : "application/octet-stream";
|
||||||
|
this.headers = (headers != null) ? new LinkedHashMap<>(headers) : Collections.emptyMap();
|
||||||
|
this.body = (body != null) ? body : new byte[0];
|
||||||
|
this.finalUrl = finalUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void writeBody(DataOutputStream out) throws IOException {
|
||||||
|
out.writeInt(statusCode);
|
||||||
|
out.writeUTF((contentType != null) ? contentType : "application/octet-stream");
|
||||||
|
|
||||||
|
writeStringMap(out, headers);
|
||||||
|
|
||||||
|
out.writeBoolean(finalUrl != null);
|
||||||
|
if (finalUrl != null) out.writeUTF(finalUrl);
|
||||||
|
|
||||||
|
byte[] b = (body != null) ? body : new byte[0];
|
||||||
|
out.writeInt(b.length);
|
||||||
|
out.write(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void readBody(DataInputStream in, UUID clientID) throws IOException {
|
||||||
|
this.statusCode = in.readInt();
|
||||||
|
this.contentType = in.readUTF();
|
||||||
|
|
||||||
|
this.headers = readStringMap(in);
|
||||||
|
|
||||||
|
boolean hasFinal = in.readBoolean();
|
||||||
|
this.finalUrl = hasFinal ? in.readUTF() : null;
|
||||||
|
|
||||||
|
int len = in.readInt();
|
||||||
|
if (len < 0) throw new IOException("Negative body length in WebResourceResponsePacket");
|
||||||
|
this.body = in.readNBytes(len);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.stream;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.WebPacket;
|
||||||
|
import org.openautonomousconnection.protocol.versions.ProtocolVersion;
|
||||||
|
import org.openautonomousconnection.protocol.versions.v1_0_1.beta.WebPacketHeader;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stream body chunk.
|
||||||
|
*
|
||||||
|
* <p>Correlation is done via {@link WebPacketHeader#getRequestId()}.</p>
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
public final class WebStreamChunkPacket_v1_0_1_B extends WebPacket {
|
||||||
|
private int seq;
|
||||||
|
private byte[] data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registration constructor.
|
||||||
|
*/
|
||||||
|
public WebStreamChunkPacket_v1_0_1_B() {
|
||||||
|
super(15, ProtocolVersion.PV_1_0_1_BETA);
|
||||||
|
}
|
||||||
|
|
||||||
|
public WebStreamChunkPacket_v1_0_1_B(WebPacketHeader header, int seq, byte[] data) {
|
||||||
|
this();
|
||||||
|
setHeader(header);
|
||||||
|
this.seq = seq;
|
||||||
|
this.data = (data != null) ? data : new byte[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void writeBody(DataOutputStream out) throws IOException {
|
||||||
|
out.writeInt(seq);
|
||||||
|
byte[] b = (data != null) ? data : new byte[0];
|
||||||
|
out.writeInt(b.length);
|
||||||
|
out.write(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void readBody(DataInputStream in, UUID clientID) throws IOException {
|
||||||
|
this.seq = in.readInt();
|
||||||
|
int len = in.readInt();
|
||||||
|
if (len < 0) throw new IOException("Negative chunk length in WebStreamChunkPacket");
|
||||||
|
this.data = in.readNBytes(len);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.stream;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.WebPacket;
|
||||||
|
import org.openautonomousconnection.protocol.versions.ProtocolVersion;
|
||||||
|
import org.openautonomousconnection.protocol.versions.v1_0_1.beta.WebPacketHeader;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ends a streamed body transfer.
|
||||||
|
*
|
||||||
|
* <p>Correlation is done via {@link WebPacketHeader#getRequestId()}.</p>
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
public final class WebStreamEndPacket_v1_0_1_B extends WebPacket {
|
||||||
|
private boolean ok;
|
||||||
|
private String error;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registration constructor.
|
||||||
|
*/
|
||||||
|
public WebStreamEndPacket_v1_0_1_B() {
|
||||||
|
super(16, ProtocolVersion.PV_1_0_1_BETA);
|
||||||
|
}
|
||||||
|
|
||||||
|
public WebStreamEndPacket_v1_0_1_B(WebPacketHeader header, boolean ok, String error) {
|
||||||
|
this();
|
||||||
|
setHeader(header);
|
||||||
|
this.ok = ok;
|
||||||
|
this.error = error;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void writeBody(DataOutputStream out) throws IOException {
|
||||||
|
out.writeBoolean(ok);
|
||||||
|
out.writeBoolean(error != null);
|
||||||
|
if (error != null) out.writeUTF(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void readBody(DataInputStream in, UUID clientID) throws IOException {
|
||||||
|
this.ok = in.readBoolean();
|
||||||
|
boolean hasErr = in.readBoolean();
|
||||||
|
this.error = hasErr ? in.readUTF() : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
package org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.stream;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.WebPacket;
|
||||||
|
import org.openautonomousconnection.protocol.versions.ProtocolVersion;
|
||||||
|
import org.openautonomousconnection.protocol.versions.v1_0_1.beta.WebPacketHeader;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts a streamed response body.
|
||||||
|
*
|
||||||
|
* <p>Correlation is done via {@link WebPacketHeader#getRequestId()}.</p>
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
public final class WebStreamStartPacket_v1_0_1_B extends WebPacket {
|
||||||
|
private int statusCode;
|
||||||
|
private String contentType;
|
||||||
|
private Map<String, String> headers;
|
||||||
|
private long totalLength;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registration constructor.
|
||||||
|
*/
|
||||||
|
public WebStreamStartPacket_v1_0_1_B() {
|
||||||
|
super(14, ProtocolVersion.PV_1_0_1_BETA);
|
||||||
|
}
|
||||||
|
|
||||||
|
public WebStreamStartPacket_v1_0_1_B(WebPacketHeader header, int statusCode, String contentType, Map<String, String> headers, long totalLength) {
|
||||||
|
this();
|
||||||
|
setHeader(header);
|
||||||
|
this.statusCode = statusCode;
|
||||||
|
this.contentType = (contentType != null) ? contentType : "application/octet-stream";
|
||||||
|
this.headers = (headers != null) ? new LinkedHashMap<>(headers) : Collections.emptyMap();
|
||||||
|
this.totalLength = totalLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void writeBody(DataOutputStream out) throws IOException {
|
||||||
|
out.writeInt(statusCode);
|
||||||
|
out.writeUTF((contentType != null) ? contentType : "application/octet-stream");
|
||||||
|
writeStringMap(out, headers);
|
||||||
|
out.writeLong(totalLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void readBody(DataInputStream in, UUID clientID) throws IOException {
|
||||||
|
this.statusCode = in.readInt();
|
||||||
|
this.contentType = in.readUTF();
|
||||||
|
this.headers = readStringMap(in);
|
||||||
|
this.totalLength = in.readLong();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.openautonomousconnection.protocol.side.client;
|
package org.openautonomousconnection.protocol.side.client;
|
||||||
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.event.EventListener;
|
import dev.unlegitdqrk.unlegitlibrary.event.EventListener;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.event.EventPriority;
|
||||||
import dev.unlegitdqrk.unlegitlibrary.event.Listener;
|
import dev.unlegitdqrk.unlegitlibrary.event.Listener;
|
||||||
import dev.unlegitdqrk.unlegitlibrary.file.FileUtils;
|
import dev.unlegitdqrk.unlegitlibrary.file.FileUtils;
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.client.NetworkClient;
|
import dev.unlegitdqrk.unlegitlibrary.network.system.client.NetworkClient;
|
||||||
@@ -174,7 +175,7 @@ public abstract class ProtocolClient extends EventListener {
|
|||||||
if (this.insVersion == null) this.insVersion = insVersion;
|
if (this.insVersion == null) this.insVersion = insVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Listener
|
@Listener(priority = EventPriority.HIGHEST)
|
||||||
public final void onDisconnect(ClientDisconnectedEvent event) {
|
public final void onDisconnect(ClientDisconnectedEvent event) {
|
||||||
if (clientToINS == null || !clientToINS.isConnected()) {
|
if (clientToINS == null || !clientToINS.isConnected()) {
|
||||||
insVersion = null;
|
insVersion = null;
|
||||||
@@ -182,7 +183,7 @@ public abstract class ProtocolClient extends EventListener {
|
|||||||
disconnectFromServer();
|
disconnectFromServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (clientToServer != null && !clientToServer.isConnected()) {
|
if (clientToServer == null || !clientToServer.isConnected()) {
|
||||||
serverVersion = null;
|
serverVersion = null;
|
||||||
clientToServer = null;
|
clientToServer = null;
|
||||||
}
|
}
|
||||||
@@ -254,7 +255,7 @@ public abstract class ProtocolClient extends EventListener {
|
|||||||
yes = compatibleVersion.getProtocolType() == ProtocolVersion.ProtocolType.STABLE;
|
yes = compatibleVersion.getProtocolType() == ProtocolVersion.ProtocolType.STABLE;
|
||||||
if (yes) break;
|
if (yes) break;
|
||||||
}
|
}
|
||||||
return isBetaServer() || yes;
|
return isStableServer() || yes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final boolean isBetaServer() {
|
public final boolean isBetaServer() {
|
||||||
@@ -267,7 +268,7 @@ public abstract class ProtocolClient extends EventListener {
|
|||||||
yes = compatibleVersion.getProtocolType() == ProtocolVersion.ProtocolType.BETA;
|
yes = compatibleVersion.getProtocolType() == ProtocolVersion.ProtocolType.BETA;
|
||||||
if (yes) break;
|
if (yes) break;
|
||||||
}
|
}
|
||||||
return isStableServer() || yes;
|
return isBetaServer() || yes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final boolean isClassicServer() {
|
public final boolean isClassicServer() {
|
||||||
@@ -312,6 +313,7 @@ public abstract class ProtocolClient extends EventListener {
|
|||||||
|
|
||||||
private final void disconnectFromServer() {
|
private final void disconnectFromServer() {
|
||||||
if (clientToServer != null) {
|
if (clientToServer != null) {
|
||||||
|
if (clientToINS == null || !clientToINS.isConnected())
|
||||||
protocolBridge.getProtocolValues().eventManager.unregisterListener(this);
|
protocolBridge.getProtocolValues().eventManager.unregisterListener(this);
|
||||||
clientToServer.disconnect();
|
clientToServer.disconnect();
|
||||||
clientToServer = null;
|
clientToServer = null;
|
||||||
|
|||||||
@@ -0,0 +1,407 @@
|
|||||||
|
package org.openautonomousconnection.protocol.side.client;
|
||||||
|
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.event.EventPriority;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.event.Listener;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.client.events.packets.C_PacketReadEvent;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.TransportProtocol;
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.WebRequestPacket;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.WebResponsePacket;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.stream.WebStreamChunkPacket_v1_0_0_B;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.stream.WebStreamEndPacket_v1_0_0_B;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.stream.WebStreamStartPacket_v1_0_0_B;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.WebPacket;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.document.WebDocumentApplyRequestPacket;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.document.WebDocumentApplyResponsePacket;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.document.WebDocumentSnapshotEventPacket;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.navigate.WebNavigateAckPacket;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.navigate.WebNavigateRequestPacket;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.resource.WebResourceRequestPacket;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.resource.WebResourceResponsePacket;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.stream.WebStreamChunkPacket_v1_0_1_B;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.stream.WebStreamEndPacket_v1_0_1_B;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.stream.WebStreamStartPacket_v1_0_1_B;
|
||||||
|
import org.openautonomousconnection.protocol.versions.ProtocolVersion;
|
||||||
|
import org.openautonomousconnection.protocol.versions.v1_0_0.beta.WebRequestMethod;
|
||||||
|
import org.openautonomousconnection.protocol.versions.v1_0_1.beta.*;
|
||||||
|
import org.openautonomousconnection.protocol.versions.v1_0_1.beta.compat.WebCompatMapper;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Web-capable protocol client built on top of {@link ProtocolClient}.
|
||||||
|
*
|
||||||
|
* <p>Compatibility strategy:</p>
|
||||||
|
* <ul>
|
||||||
|
* <li>In Web v1.0.0 mode, correlation is only possible if there is exactly ONE in-flight request.</li>
|
||||||
|
* <li>This client therefore enforces single in-flight request and maps responses/streams
|
||||||
|
* onto that single active {@link WebPacketHeader}.</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
public abstract class ProtocolWebClient extends ProtocolClient {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Web header factory bound to this client instance.
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
private final WebHeaderFactory webHeaderFactory = new WebHeaderFactory();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tab contexts by tabId (stable).
|
||||||
|
*/
|
||||||
|
private final Map<Long, WebHeaderFactory.WebTabContext> tabs = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Single in-flight correlation header.
|
||||||
|
*
|
||||||
|
* <p>v1.0.0 has no correlation fields; without changing the v1.0.0 server this is the only deterministic mapping.</p>
|
||||||
|
*/
|
||||||
|
private final AtomicReference<WebPacketHeader> v100BInFlight = new AtomicReference<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True once we observed a 1.0.0 stream start for the current in-flight request.
|
||||||
|
*/
|
||||||
|
private volatile boolean v100BStreaming;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates and registers a new tab context.
|
||||||
|
*/
|
||||||
|
public final WebHeaderFactory.WebTabContext createTab() {
|
||||||
|
WebHeaderFactory.WebTabContext tab = webHeaderFactory.createTab();
|
||||||
|
tabs.put(tab.getTabId(), tab);
|
||||||
|
return tab;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a tab context.
|
||||||
|
*/
|
||||||
|
public final void closeTab(long tabId) {
|
||||||
|
tabs.remove(tabId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the tab context for a tab id.
|
||||||
|
*/
|
||||||
|
public final WebHeaderFactory.WebTabContext getTab(long tabId) {
|
||||||
|
return tabs.get(tabId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Begins a new navigation for the given tab.
|
||||||
|
*/
|
||||||
|
public final long beginNavigation(WebHeaderFactory.WebTabContext tab) {
|
||||||
|
Objects.requireNonNull(tab, "tab");
|
||||||
|
return webHeaderFactory.beginNavigation(tab);
|
||||||
|
}
|
||||||
|
|
||||||
|
public final void sendNavigate(
|
||||||
|
WebHeaderFactory.WebTabContext tab,
|
||||||
|
String url,
|
||||||
|
String referrer,
|
||||||
|
WebTransitionType transition,
|
||||||
|
long headerProfileId,
|
||||||
|
WebCacheMode cacheMode,
|
||||||
|
boolean noStore
|
||||||
|
) throws Exception {
|
||||||
|
Objects.requireNonNull(tab, "tab");
|
||||||
|
ensureServerConnected();
|
||||||
|
|
||||||
|
WebPacketHeader header = webHeaderFactory.navigation(tab, noStore);
|
||||||
|
header = new WebPacketHeader(
|
||||||
|
header.getRequestId(),
|
||||||
|
header.getTabId(),
|
||||||
|
header.getPageId(),
|
||||||
|
header.getFrameId(),
|
||||||
|
header.getFlags() | WebPacketFlags.NAVIGATION,
|
||||||
|
header.getTimestampMs()
|
||||||
|
);
|
||||||
|
|
||||||
|
if (is100BServer()) {
|
||||||
|
begin100BRequest(header);
|
||||||
|
WebRequestPacket packet = new WebRequestPacket(
|
||||||
|
url,
|
||||||
|
WebRequestMethod.GET,
|
||||||
|
Collections.emptyMap(),
|
||||||
|
null
|
||||||
|
);
|
||||||
|
getClientServerConnection().sendPacket(packet, TransportProtocol.TCP);
|
||||||
|
onWebRequestSent(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
WebNavigateRequestPacket pkt = new WebNavigateRequestPacket(
|
||||||
|
header,
|
||||||
|
url,
|
||||||
|
referrer,
|
||||||
|
transition,
|
||||||
|
headerProfileId,
|
||||||
|
cacheMode
|
||||||
|
);
|
||||||
|
|
||||||
|
getClientServerConnection().sendPacket(pkt, TransportProtocol.TCP);
|
||||||
|
onWebRequestSent(pkt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public final long sendResource(
|
||||||
|
WebHeaderFactory.WebTabContext tab,
|
||||||
|
long frameId,
|
||||||
|
String url,
|
||||||
|
String method,
|
||||||
|
Map<String, String> headers,
|
||||||
|
byte[] body,
|
||||||
|
String bodyContentType,
|
||||||
|
WebInitiatorType initiator,
|
||||||
|
WebCacheMode cacheMode,
|
||||||
|
boolean noStore
|
||||||
|
) throws Exception {
|
||||||
|
Objects.requireNonNull(tab, "tab");
|
||||||
|
ensureServerConnected();
|
||||||
|
|
||||||
|
WebPacketHeader base = webHeaderFactory.resource(tab, frameId, noStore);
|
||||||
|
WebPacketHeader header = new WebPacketHeader(
|
||||||
|
base.getRequestId(),
|
||||||
|
base.getTabId(),
|
||||||
|
base.getPageId(),
|
||||||
|
base.getFrameId(),
|
||||||
|
base.getFlags() | WebPacketFlags.RESOURCE,
|
||||||
|
base.getTimestampMs()
|
||||||
|
);
|
||||||
|
|
||||||
|
if (is100BServer()) {
|
||||||
|
begin100BRequest(header);
|
||||||
|
|
||||||
|
WebRequestPacket packet = new WebRequestPacket(
|
||||||
|
url,
|
||||||
|
WebCompatMapper.map100BMethod(method),
|
||||||
|
headers != null ? headers : Collections.emptyMap(),
|
||||||
|
body
|
||||||
|
);
|
||||||
|
|
||||||
|
getClientServerConnection().sendPacket(packet, TransportProtocol.TCP);
|
||||||
|
onWebRequestSent(null);
|
||||||
|
return header.getRequestId();
|
||||||
|
}
|
||||||
|
|
||||||
|
WebResourceRequestPacket pkt = new WebResourceRequestPacket(
|
||||||
|
header,
|
||||||
|
url,
|
||||||
|
method,
|
||||||
|
headers,
|
||||||
|
body,
|
||||||
|
bodyContentType,
|
||||||
|
initiator,
|
||||||
|
cacheMode
|
||||||
|
);
|
||||||
|
|
||||||
|
getClientServerConnection().sendPacket(pkt, TransportProtocol.TCP);
|
||||||
|
onWebRequestSent(pkt);
|
||||||
|
return header.getRequestId();
|
||||||
|
}
|
||||||
|
|
||||||
|
public final long sendDocumentApply(WebHeaderFactory.WebTabContext tab, long frameId, String fullHtml) throws Exception {
|
||||||
|
Objects.requireNonNull(tab, "tab");
|
||||||
|
ensureServerConnected();
|
||||||
|
|
||||||
|
if (is100BServer()) {
|
||||||
|
throw new UnsupportedOperationException("Document apply is not supported by Web v1.0.0 servers.");
|
||||||
|
}
|
||||||
|
|
||||||
|
WebPacketHeader base = webHeaderFactory.documentApply(tab, frameId);
|
||||||
|
WebPacketHeader header = new WebPacketHeader(
|
||||||
|
base.getRequestId(),
|
||||||
|
base.getTabId(),
|
||||||
|
base.getPageId(),
|
||||||
|
base.getFrameId(),
|
||||||
|
base.getFlags(),
|
||||||
|
base.getTimestampMs()
|
||||||
|
);
|
||||||
|
|
||||||
|
WebDocumentApplyRequestPacket pkt = new WebDocumentApplyRequestPacket(header, fullHtml);
|
||||||
|
getClientServerConnection().sendPacket(pkt, TransportProtocol.TCP);
|
||||||
|
onWebRequestSent(pkt);
|
||||||
|
return pkt.getHeader().getRequestId();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stale detection: drops packets that refer to an old pageId for the same tab.
|
||||||
|
*/
|
||||||
|
public final boolean isStale(WebPacketHeader header) {
|
||||||
|
if (header == null) return false;
|
||||||
|
WebHeaderFactory.WebTabContext tab = tabs.get(header.getTabId());
|
||||||
|
if (tab == null) return false;
|
||||||
|
return webHeaderFactory.isStale(tab, header);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook invoked after sending a web request packet (may be null in 1.0.0 mode).
|
||||||
|
*/
|
||||||
|
protected void onWebRequestSent(WebPacket packet) {
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onNavigateAck(WebNavigateAckPacket packet) {
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onResourceResponse(WebResourceResponsePacket packet) {
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onStreamStart(WebStreamStartPacket_v1_0_1_B packet) {
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onStreamChunk(WebStreamChunkPacket_v1_0_1_B packet) {
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onStreamEnd(WebStreamEndPacket_v1_0_1_B packet) {
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onDocumentSnapshot(WebDocumentSnapshotEventPacket packet) {
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onDocumentApplyResponse(WebDocumentApplyResponsePacket packet) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Listener(priority = EventPriority.HIGHEST)
|
||||||
|
public final void onPacket(C_PacketReadEvent event) {
|
||||||
|
Packet p = event.getPacket();
|
||||||
|
|
||||||
|
// Native v1.0.1 packets
|
||||||
|
if (p instanceof WebPacket packet) {
|
||||||
|
handleIncoming(packet);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is100BServer()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1.0.0 mapping requires an active correlation
|
||||||
|
WebPacketHeader corr = v100BInFlight.get();
|
||||||
|
if (corr == null) {
|
||||||
|
// Deterministic mapping is impossible without correlation.
|
||||||
|
// Dropping is safer than misrouting.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// v1.0.0 response -> v1.0.1 resource response
|
||||||
|
if (p instanceof WebResponsePacket resp) {
|
||||||
|
WebResourceResponsePacket mapped = WebCompatMapper.toV101ResourceResponse(corr, resp);
|
||||||
|
onResourceResponse(mapped);
|
||||||
|
|
||||||
|
// If this response is not part of a stream, release correlation.
|
||||||
|
if (!looksLike100BStreamHandshake(resp) && !v100BStreaming) {
|
||||||
|
end100BRequest();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// v1.0.0 stream start/chunk/end -> v1.0.1 stream packets
|
||||||
|
if (p instanceof WebStreamStartPacket_v1_0_0_B start) {
|
||||||
|
v100BStreaming = true;
|
||||||
|
WebStreamStartPacket_v1_0_1_B mapped = WebCompatMapper.toV101StreamStart(corr, start);
|
||||||
|
onStreamStart(mapped);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p instanceof WebStreamChunkPacket_v1_0_0_B chunk) {
|
||||||
|
WebStreamChunkPacket_v1_0_1_B mapped = WebCompatMapper.toV101StreamChunk(corr, chunk);
|
||||||
|
onStreamChunk(mapped);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p instanceof WebStreamEndPacket_v1_0_0_B end) {
|
||||||
|
WebStreamEndPacket_v1_0_1_B mapped = WebCompatMapper.toV101StreamEnd(corr, end);
|
||||||
|
onStreamEnd(mapped);
|
||||||
|
|
||||||
|
// stream finished -> release correlation
|
||||||
|
end100BRequest();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Central dispatcher for incoming v1.0.1 web packets.
|
||||||
|
*/
|
||||||
|
public final void handleIncoming(WebPacket packet) {
|
||||||
|
if (packet == null) return;
|
||||||
|
|
||||||
|
WebPacketHeader h = packet.getHeader();
|
||||||
|
if (isStale(h)) return;
|
||||||
|
|
||||||
|
switch (packet) {
|
||||||
|
case WebNavigateAckPacket p -> onNavigateAck(p);
|
||||||
|
case WebResourceResponsePacket p -> onResourceResponse(p);
|
||||||
|
case WebStreamStartPacket_v1_0_1_B p -> onStreamStart(p);
|
||||||
|
case WebStreamChunkPacket_v1_0_1_B p -> onStreamChunk(p);
|
||||||
|
case WebStreamEndPacket_v1_0_1_B p -> onStreamEnd(p);
|
||||||
|
case WebDocumentSnapshotEventPacket p -> onDocumentSnapshot(p);
|
||||||
|
case WebDocumentApplyResponsePacket p -> onDocumentApplyResponse(p);
|
||||||
|
default -> {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ensureServerConnected() {
|
||||||
|
if (getClientServerConnection() == null) throw new IllegalStateException("Server connection is not built");
|
||||||
|
if (!getClientServerConnection().isConnected() && !getClientServerConnection().isTCPConnected()) {
|
||||||
|
throw new IllegalStateException("Server connection is not connected");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean is100BServer() {
|
||||||
|
return getServerVersion().equals(ProtocolVersion.PV_1_0_0_BETA);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Begins a deterministic 1.0.0 request mapping.
|
||||||
|
*
|
||||||
|
* <p>Enforces single in-flight 1.0.0 request.</p>
|
||||||
|
*
|
||||||
|
* @param correlation correlation header to use for mapping 1.0.0 responses
|
||||||
|
*/
|
||||||
|
private void begin100BRequest(WebPacketHeader correlation) {
|
||||||
|
Objects.requireNonNull(correlation, "correlation");
|
||||||
|
v100BStreaming = false;
|
||||||
|
|
||||||
|
if (!v100BInFlight.compareAndSet(null, correlation)) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Web v1.0.0 mode supports only 1 in-flight request. " +
|
||||||
|
"Wait for response/stream end before sending the next request."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Releases 1.0.0 correlation state.
|
||||||
|
*/
|
||||||
|
private void end100BRequest() {
|
||||||
|
v100BStreaming = false;
|
||||||
|
v100BInFlight.set(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Heuristic: checks if a 1.0.0 response indicates streaming will follow.
|
||||||
|
*
|
||||||
|
* <p>Your v1.0.0 WebServer uses: header 'x-oac-stream' = '1' for the 202 response before streaming.</p>
|
||||||
|
*
|
||||||
|
* @param resp 1.0.0 response
|
||||||
|
* @return true if likely stream handshake
|
||||||
|
*/
|
||||||
|
private boolean looksLike100BStreamHandshake(WebResponsePacket resp) {
|
||||||
|
if (resp == null) return false;
|
||||||
|
Map<String, String> h = resp.getHeaders();
|
||||||
|
if (h == null) return false;
|
||||||
|
|
||||||
|
// Case-insensitive lookup without allocating
|
||||||
|
for (Map.Entry<String, String> e : h.entrySet()) {
|
||||||
|
if (e.getKey() != null && e.getValue() != null
|
||||||
|
&& e.getKey().equalsIgnoreCase("x-oac-stream")
|
||||||
|
&& e.getValue().equals("1")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -38,8 +38,8 @@ public class CustomConnectedClient extends EventListener {
|
|||||||
clientVersion = null;
|
clientVersion = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Listener(priority = EventPriority.HIGH)
|
@Listener(priority = EventPriority.LOWEST)
|
||||||
public void onDisconnect(S_ClientDisconnectedEvent event) {
|
public final void onDisconnect(S_ClientDisconnectedEvent event) {
|
||||||
if (event.getClient().getUniqueID().equals(this.connection.getUniqueID())) {
|
if (event.getClient().getUniqueID().equals(this.connection.getUniqueID())) {
|
||||||
server.getProtocolBridge().getProtocolValues().eventManager.unregisterListener(this);
|
server.getProtocolBridge().getProtocolValues().eventManager.unregisterListener(this);
|
||||||
clientVersion = null;
|
clientVersion = null;
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ public abstract class ProtocolCustomServer extends EventListener {
|
|||||||
trustStore.setCertificateEntry("root-ca-" + (caIndex++), caCert);
|
trustStore.setCertificateEntry("root-ca-" + (caIndex++), caCert);
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Build server key material (private key + certificate chain) ---
|
// Build server key material (private key + certificate chain)
|
||||||
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||||
keyStore.load(null, null);
|
keyStore.load(null, null);
|
||||||
|
|
||||||
@@ -209,8 +209,8 @@ public abstract class ProtocolCustomServer extends EventListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Listener
|
@Listener(priority = EventPriority.HIGHEST)
|
||||||
public void onStop(ServerStoppedEvent event) {
|
public final void onStop(ServerStoppedEvent event) {
|
||||||
if (event.getServer() == network) {
|
if (event.getServer() == network) {
|
||||||
protocolBridge.getProtocolValues().eventManager.unregisterListener(this);
|
protocolBridge.getProtocolValues().eventManager.unregisterListener(this);
|
||||||
}
|
}
|
||||||
@@ -270,7 +270,7 @@ public abstract class ProtocolCustomServer extends EventListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Listener(priority = EventPriority.LOW)
|
@Listener(priority = EventPriority.HIGHEST)
|
||||||
public final void clientDisconnected(S_ClientDisconnectedEvent event) {
|
public final void clientDisconnected(S_ClientDisconnectedEvent event) {
|
||||||
for (CustomConnectedClient client : new ArrayList<>(clients)) {
|
for (CustomConnectedClient client : new ArrayList<>(clients)) {
|
||||||
if (client.getConnection().getUniqueID().equals(event.getClient().getUniqueID())) {
|
if (client.getConnection().getUniqueID().equals(event.getClient().getUniqueID())) {
|
||||||
|
|||||||
@@ -1,131 +1,220 @@
|
|||||||
package org.openautonomousconnection.protocol.side.web;
|
package org.openautonomousconnection.protocol.side.web;
|
||||||
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.file.FileUtils;
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.string.RandomString;
|
|
||||||
import org.openautonomousconnection.protocol.annotations.ProtocolInfo;
|
import org.openautonomousconnection.protocol.annotations.ProtocolInfo;
|
||||||
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.WebRequestPacket;
|
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.WebRequestPacket;
|
||||||
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.WebResponsePacket;
|
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.WebResponsePacket;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.document.WebDocumentApplyRequestPacket;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.document.WebDocumentApplyResponsePacket;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.navigate.WebNavigateAckPacket;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.navigate.WebNavigateRequestPacket;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.resource.WebResourceRequestPacket;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.resource.WebResourceResponsePacket;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.stream.WebStreamChunkPacket_v1_0_1_B;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.stream.WebStreamEndPacket_v1_0_1_B;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.stream.WebStreamStartPacket_v1_0_1_B;
|
||||||
import org.openautonomousconnection.protocol.side.server.CustomConnectedClient;
|
import org.openautonomousconnection.protocol.side.server.CustomConnectedClient;
|
||||||
import org.openautonomousconnection.protocol.side.server.ProtocolCustomServer;
|
|
||||||
import org.openautonomousconnection.protocol.side.web.managers.AuthManager;
|
|
||||||
import org.openautonomousconnection.protocol.side.web.managers.RuleManager;
|
|
||||||
import org.openautonomousconnection.protocol.versions.ProtocolVersion;
|
import org.openautonomousconnection.protocol.versions.ProtocolVersion;
|
||||||
|
import org.openautonomousconnection.protocol.versions.v1_0_0.beta.ProtocolWebServer_1_0_0_B;
|
||||||
|
import org.openautonomousconnection.protocol.versions.v1_0_1.beta.WebPacketFlags;
|
||||||
|
import org.openautonomousconnection.protocol.versions.v1_0_1.beta.WebPacketHeader;
|
||||||
|
import org.openautonomousconnection.protocol.versions.v1_0_1.beta.compat.WebCompatMapper;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.Random;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the web server for the protocol.
|
* Protocol Web Server base for Web v1.0.1 (beta) with built-in v1.0.0 compatibility.
|
||||||
|
*
|
||||||
|
* <p>This class keeps the v1.0.0 entry point ({@link #onWebRequest(CustomConnectedClient, WebRequestPacket)})
|
||||||
|
* and adapts it to the v1.0.1 resource pipeline by mapping v1.0.0 requests to a synthetic
|
||||||
|
* {@link WebResourceRequestPacket} and mapping v1.0.1 responses back to {@link WebResponsePacket}.</p>
|
||||||
|
*
|
||||||
|
* <p>Important:
|
||||||
|
* <ul>
|
||||||
|
* <li>This class does NOT perform network packet dispatch automatically. Your server packet-receive listener
|
||||||
|
* must call the appropriate {@code handle*} methods (v1.0.1) or let the v1.0.0 pipeline call {@code onWebRequest}.</li>
|
||||||
|
* <li>Responses/streams must preserve correlation via {@link WebPacketHeader#getRequestId()} for v1.0.1 clients.</li>
|
||||||
|
* <li>v1.0.0 streaming packets cannot carry request correlation. If you stream, you must
|
||||||
|
* send 1.0.0 stream packets to 1.0.0 clients and v1.0.1 stream packets to v1.0.1 clients.</li>
|
||||||
|
* </ul>
|
||||||
|
* </p>
|
||||||
*/
|
*/
|
||||||
@ProtocolInfo(protocolSide = ProtocolVersion.ProtocolSide.WEB)
|
@ProtocolInfo(protocolSide = ProtocolVersion.ProtocolSide.WEB)
|
||||||
public abstract class ProtocolWebServer extends ProtocolCustomServer {
|
public abstract class ProtocolWebServer extends ProtocolWebServer_1_0_0_B {
|
||||||
/**
|
|
||||||
* Folder for web content.
|
private static final long LEGACY_TAB_ID = 1L;
|
||||||
*/
|
private static final long LEGACY_PAGE_ID = 1L;
|
||||||
private final File contentFolder;
|
|
||||||
/**
|
|
||||||
* Folder for error pages.
|
|
||||||
*/
|
|
||||||
private final File errorsFolder;
|
|
||||||
/**
|
|
||||||
* A unique secret for session management.
|
|
||||||
*/
|
|
||||||
private final String uniqueSessionString;
|
|
||||||
/**
|
|
||||||
* The expiration time of a Session in minutes
|
|
||||||
*/
|
|
||||||
private final int sessionExpire;
|
|
||||||
/**
|
|
||||||
* The max upload size in MB
|
|
||||||
*/
|
|
||||||
private final int maxUploadSize;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the web server with the given configuration, authentication, and rules files.
|
* Initializes the web server with the given configuration, authentication, and rules files.
|
||||||
*
|
*
|
||||||
* @param authFile The authentication file.
|
* @param authFile The authentication file.
|
||||||
* @param rulesFile The rules file.
|
* @param rulesFile The rules file.
|
||||||
* @param sessionExpire The expiration time of a Session in minutes
|
* @param sessionExpire The expiration time of a Session in minutes.
|
||||||
* @param uploadSize The max upload size in MB
|
* @param uploadSize The max upload size in MB.
|
||||||
* @throws Exception If an error occurs during initialization.
|
* @throws Exception If an error occurs during initialization.
|
||||||
*/
|
*/
|
||||||
public ProtocolWebServer(File authFile, File rulesFile, int sessionExpire, int uploadSize) throws Exception {
|
public ProtocolWebServer(File authFile, File rulesFile, int sessionExpire, int uploadSize) throws Exception {
|
||||||
super("server", "server");
|
super(authFile, rulesFile, sessionExpire, uploadSize);
|
||||||
|
|
||||||
this.sessionExpire = sessionExpire;
|
|
||||||
this.maxUploadSize = uploadSize;
|
|
||||||
|
|
||||||
// Set up content and error folders
|
|
||||||
contentFolder = new File("content");
|
|
||||||
errorsFolder = new File("errors");
|
|
||||||
|
|
||||||
// Create folders if they don't exist
|
|
||||||
if (!contentFolder.exists()) contentFolder.mkdir();
|
|
||||||
if (!errorsFolder.exists()) errorsFolder.mkdir();
|
|
||||||
|
|
||||||
// Create auth and rules files with default content if they don't exist
|
|
||||||
if (!authFile.exists()) {
|
|
||||||
authFile.createNewFile();
|
|
||||||
FileUtils.writeFile(authFile, """
|
|
||||||
admin:5e884898da28047151d0e56f8dc6292773603d0d6aabbddab8f91d8e5f99f6c7
|
|
||||||
user:e99a18c428cb38d5f260853678922e03abd8335f
|
|
||||||
""");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create default rules file if it doesn't exist
|
|
||||||
if (!rulesFile.exists()) {
|
|
||||||
rulesFile.createNewFile();
|
|
||||||
FileUtils.writeFile(rulesFile, """
|
|
||||||
{
|
|
||||||
"allow": [
|
|
||||||
"index.html",
|
|
||||||
"css/*",
|
|
||||||
"private/info.php"
|
|
||||||
],
|
|
||||||
"deny": [
|
|
||||||
"private/*"
|
|
||||||
],
|
|
||||||
"auth": [
|
|
||||||
"private/*",
|
|
||||||
"admin/*"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
""");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load authentication and rules
|
|
||||||
uniqueSessionString = AuthManager.sha256(new RandomString(new Random(System.currentTimeMillis()).nextInt(10, 20)).nextString());
|
|
||||||
|
|
||||||
AuthManager.loadAuthFile(authFile);
|
|
||||||
RuleManager.loadRules(rulesFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
public final File getContentFolder() {
|
|
||||||
return contentFolder;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final File getErrorsFolder() {
|
|
||||||
return errorsFolder;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final String getUniqueSessionString() {
|
|
||||||
return uniqueSessionString;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final int getSessionExpire() {
|
|
||||||
return sessionExpire;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final int getMaxUploadSize() {
|
|
||||||
return maxUploadSize;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the server receives a WebRequestPacket from the client.
|
* Server-side dispatcher entry point for a navigation request (packet id 01).
|
||||||
*
|
*
|
||||||
* @param client The connected web client (pipeline + web socket).
|
* @param client connected client
|
||||||
* @param request The full decoded request packet.
|
* @param request navigation request
|
||||||
* @return The response packet that should be sent back to the client.
|
* @return navigation ack to send back
|
||||||
*/
|
*/
|
||||||
public abstract WebResponsePacket onWebRequest(CustomConnectedClient client, WebRequestPacket request);
|
public final WebNavigateAckPacket handleNavigate(CustomConnectedClient client, WebNavigateRequestPacket request) {
|
||||||
|
Objects.requireNonNull(client, "client");
|
||||||
|
Objects.requireNonNull(request, "request");
|
||||||
|
|
||||||
|
WebNavigateAckPacket ack = onNavigateRequest(client, request);
|
||||||
|
if (ack == null) {
|
||||||
|
ack = new WebNavigateAckPacket(
|
||||||
|
mirrorHeader(request.getHeader(), WebPacketFlags.NAVIGATION),
|
||||||
|
false,
|
||||||
|
"onNavigateRequest returned null"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return ack;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Server-side dispatcher entry point for a resource request (packet id 03).
|
||||||
|
*
|
||||||
|
* <p>Streaming:
|
||||||
|
* <ul>
|
||||||
|
* <li>If you want to stream the body, return a {@link WebResourceResponsePacket} with an empty body,
|
||||||
|
* and then send {@link WebStreamStartPacket_v1_0_1_B}/{@link WebStreamChunkPacket_v1_0_1_B}/{@link WebStreamEndPacket_v1_0_1_B}
|
||||||
|
* using the SAME {@code requestId}.</li>
|
||||||
|
* </ul>
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param client connected client
|
||||||
|
* @param request resource request
|
||||||
|
* @return resource response packet to send back
|
||||||
|
*/
|
||||||
|
public final WebResourceResponsePacket handleResource(CustomConnectedClient client, WebResourceRequestPacket request) {
|
||||||
|
Objects.requireNonNull(client, "client");
|
||||||
|
Objects.requireNonNull(request, "request");
|
||||||
|
|
||||||
|
WebResourceResponsePacket resp = onResourceRequest(client, request);
|
||||||
|
if (resp == null) {
|
||||||
|
resp = new WebResourceResponsePacket(
|
||||||
|
mirrorHeader(request.getHeader(), WebPacketFlags.RESOURCE),
|
||||||
|
500,
|
||||||
|
"text/plain; charset=utf-8",
|
||||||
|
java.util.Collections.emptyMap(),
|
||||||
|
"onResourceRequest returned null".getBytes(StandardCharsets.UTF_8),
|
||||||
|
null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Server-side dispatcher entry point for a document apply request (packet id 20).
|
||||||
|
*
|
||||||
|
* @param client connected client
|
||||||
|
* @param request apply request
|
||||||
|
* @return apply response
|
||||||
|
*/
|
||||||
|
public final WebDocumentApplyResponsePacket handleDocumentApply(CustomConnectedClient client, WebDocumentApplyRequestPacket request) {
|
||||||
|
Objects.requireNonNull(client, "client");
|
||||||
|
Objects.requireNonNull(request, "request");
|
||||||
|
|
||||||
|
WebDocumentApplyResponsePacket resp = onDocumentApplyRequest(client, request);
|
||||||
|
if (resp == null) {
|
||||||
|
resp = new WebDocumentApplyResponsePacket(
|
||||||
|
mirrorHeader(request.getHeader(), 0),
|
||||||
|
false,
|
||||||
|
"onDocumentApplyRequest returned null"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook: called when the server receives a navigation request.
|
||||||
|
*
|
||||||
|
* @param client connected client
|
||||||
|
* @param request navigation request
|
||||||
|
* @return ack packet (must include mirrored requestId/tabId/pageId/frameId)
|
||||||
|
*/
|
||||||
|
protected abstract WebNavigateAckPacket onNavigateRequest(CustomConnectedClient client, WebNavigateRequestPacket request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook: called when the server receives a resource request.
|
||||||
|
*
|
||||||
|
* @param client connected client
|
||||||
|
* @param request resource request
|
||||||
|
* @return response packet (must include mirrored requestId/tabId/pageId/frameId)
|
||||||
|
*/
|
||||||
|
protected abstract WebResourceResponsePacket onResourceRequest(CustomConnectedClient client, WebResourceRequestPacket request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook: called when the server receives a document apply request.
|
||||||
|
*
|
||||||
|
* @param client connected client
|
||||||
|
* @param request apply request
|
||||||
|
* @return apply response packet (must include mirrored requestId/tabId/pageId/frameId)
|
||||||
|
*/
|
||||||
|
protected abstract WebDocumentApplyResponsePacket onDocumentApplyRequest(CustomConnectedClient client, WebDocumentApplyRequestPacket request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* v1.0.0 entry point.
|
||||||
|
*
|
||||||
|
* <p>This method is invoked by the v1.0.0 server pipeline when a 1.0.0 client sends a 1.0.0 request packet.
|
||||||
|
* We adapt it into the v1.0.1 resource pipeline.</p>
|
||||||
|
*
|
||||||
|
* @param client connected client
|
||||||
|
* @param request 1.0.0 request packet
|
||||||
|
* @return 1.0.0 response packet
|
||||||
|
*/
|
||||||
|
@Deprecated(forRemoval = false, since = "1.0.1-BETA.0.1")
|
||||||
|
@Override
|
||||||
|
public WebResponsePacket onWebRequest(CustomConnectedClient client, WebRequestPacket request) {
|
||||||
|
Objects.requireNonNull(client, "client");
|
||||||
|
Objects.requireNonNull(request, "request");
|
||||||
|
|
||||||
|
// 1.0.0 requests have no correlation fields. Create synthetic ones.
|
||||||
|
long requestId = WebCompatMapper.nextCompatRequestId();
|
||||||
|
long tabId = LEGACY_TAB_ID;
|
||||||
|
long pageId = LEGACY_PAGE_ID;
|
||||||
|
|
||||||
|
WebResourceRequestPacket mapped = WebCompatMapper.toResourceRequest(requestId, tabId, pageId, request);
|
||||||
|
WebResourceResponsePacket resp = onResourceRequest(client, mapped);
|
||||||
|
|
||||||
|
if (resp == null) {
|
||||||
|
return new WebResponsePacket(
|
||||||
|
500,
|
||||||
|
"text/plain; charset=utf-8",
|
||||||
|
java.util.Collections.emptyMap(),
|
||||||
|
"onResourceRequest returned null".getBytes(StandardCharsets.UTF_8)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return WebCompatMapper.to100BResponse(resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a response header that mirrors correlation fields from an incoming header.
|
||||||
|
*
|
||||||
|
* @param incoming incoming header
|
||||||
|
* @param extraFlags extra flags to OR into the mirrored header
|
||||||
|
* @return mirrored header
|
||||||
|
*/
|
||||||
|
protected final WebPacketHeader mirrorHeader(WebPacketHeader incoming, int extraFlags) {
|
||||||
|
Objects.requireNonNull(incoming, "incoming");
|
||||||
|
return new WebPacketHeader(
|
||||||
|
incoming.getRequestId(),
|
||||||
|
incoming.getTabId(),
|
||||||
|
incoming.getPageId(),
|
||||||
|
incoming.getFrameId(),
|
||||||
|
incoming.getFlags() | extraFlags,
|
||||||
|
System.currentTimeMillis()
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -12,14 +12,19 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
public enum ProtocolVersion implements Serializable {
|
public enum ProtocolVersion implements Serializable {
|
||||||
/**
|
/**
|
||||||
* Support for old OAC-Project => <a href="https://repo.open-autonomous-connection.org/Open-Autonomous-Connection/">*_old</a>
|
* For classic OAC-Project => <a href="https://repo.open-autonomous-connection.org/Open-Autonomous-Connection/">"classic"-branch</a>
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(forRemoval = false, since = "1.0.1-BETA.0.1")
|
||||||
PV_1_0_0_CLASSIC("1.0.0", ProtocolType.CLASSIC, ProtocolSide.WEB_INS, List.of(Protocol.HTTP)),
|
PV_1_0_0_CLASSIC("1.0.0", ProtocolType.CLASSIC, ProtocolSide.WEB_INS, List.of(Protocol.HTTP)),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* First Beta Version of OAC-Protocol
|
* Version {@link ProtocolVersion#PV_1_0_0_BETA} has many bugs and does not work as expected (occurred by incompleted packets).
|
||||||
|
* Use {@link ProtocolVersion#PV_1_0_1_BETA} or newer.
|
||||||
*/
|
*/
|
||||||
PV_1_0_0_BETA("1.0.0", ProtocolType.BETA, ProtocolSide.ALL, List.of(Protocol.OAC), PV_1_0_0_CLASSIC),
|
@Deprecated(forRemoval = false, since = "1.0.1-BETA.0.1")
|
||||||
|
PV_1_0_0_BETA("1.0.0", ProtocolType.BETA, ProtocolSide.ALL, List.of(Protocol.OAC)),
|
||||||
|
|
||||||
|
PV_1_0_1_BETA("1.0.1", ProtocolType.BETA, ProtocolSide.ALL, List.of(Protocol.OAC), PV_1_0_0_BETA),
|
||||||
;
|
;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -149,9 +149,7 @@ public final class INSRecordTools {
|
|||||||
return record.ttl >= 0;
|
return record.ttl >= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
// Validation helpers
|
// Validation helpers
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates a collection of records by applying {@link #isValidRecord(INSRecord)}
|
* Validates a collection of records by applying {@link #isValidRecord(INSRecord)}
|
||||||
|
|||||||
@@ -0,0 +1,133 @@
|
|||||||
|
package org.openautonomousconnection.protocol.versions.v1_0_0.beta;
|
||||||
|
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.file.FileUtils;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.string.RandomString;
|
||||||
|
import org.openautonomousconnection.protocol.annotations.ProtocolInfo;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.WebRequestPacket;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.WebResponsePacket;
|
||||||
|
import org.openautonomousconnection.protocol.side.server.CustomConnectedClient;
|
||||||
|
import org.openautonomousconnection.protocol.side.server.ProtocolCustomServer;
|
||||||
|
import org.openautonomousconnection.protocol.side.web.managers.AuthManager;
|
||||||
|
import org.openautonomousconnection.protocol.side.web.managers.RuleManager;
|
||||||
|
import org.openautonomousconnection.protocol.versions.ProtocolVersion;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the web server for the protocol.
|
||||||
|
*/
|
||||||
|
@Deprecated(forRemoval = false, since = "1.0.1-BETA.0.1")
|
||||||
|
@ProtocolInfo(protocolSide = ProtocolVersion.ProtocolSide.WEB)
|
||||||
|
public abstract class ProtocolWebServer_1_0_0_B extends ProtocolCustomServer {
|
||||||
|
/**
|
||||||
|
* Folder for web content.
|
||||||
|
*/
|
||||||
|
private final File contentFolder;
|
||||||
|
/**
|
||||||
|
* Folder for error pages.
|
||||||
|
*/
|
||||||
|
private final File errorsFolder;
|
||||||
|
/**
|
||||||
|
* A unique secret for session management.
|
||||||
|
*/
|
||||||
|
private final String uniqueSessionString;
|
||||||
|
/**
|
||||||
|
* The expiration time of a Session in minutes
|
||||||
|
*/
|
||||||
|
private final int sessionExpire;
|
||||||
|
/**
|
||||||
|
* The max upload size in MB
|
||||||
|
*/
|
||||||
|
private final int maxUploadSize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the web server with the given configuration, authentication, and rules files.
|
||||||
|
*
|
||||||
|
* @param authFile The authentication file.
|
||||||
|
* @param rulesFile The rules file.
|
||||||
|
* @param sessionExpire The expiration time of a Session in minutes
|
||||||
|
* @param uploadSize The max upload size in MB
|
||||||
|
* @throws Exception If an error occurs during initialization.
|
||||||
|
*/
|
||||||
|
public ProtocolWebServer_1_0_0_B(File authFile, File rulesFile, int sessionExpire, int uploadSize) throws Exception {
|
||||||
|
super("server", "server");
|
||||||
|
|
||||||
|
this.sessionExpire = sessionExpire;
|
||||||
|
this.maxUploadSize = uploadSize;
|
||||||
|
|
||||||
|
// Set up content and error folders
|
||||||
|
contentFolder = new File("content");
|
||||||
|
errorsFolder = new File("errors");
|
||||||
|
|
||||||
|
// Create folders if they don't exist
|
||||||
|
if (!contentFolder.exists()) contentFolder.mkdir();
|
||||||
|
if (!errorsFolder.exists()) errorsFolder.mkdir();
|
||||||
|
|
||||||
|
// Create auth and rules files with default content if they don't exist
|
||||||
|
if (!authFile.exists()) {
|
||||||
|
authFile.createNewFile();
|
||||||
|
FileUtils.writeFile(authFile, """
|
||||||
|
admin:5e884898da28047151d0e56f8dc6292773603d0d6aabbddab8f91d8e5f99f6c7
|
||||||
|
user:e99a18c428cb38d5f260853678922e03abd8335f
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create default rules file if it doesn't exist
|
||||||
|
if (!rulesFile.exists()) {
|
||||||
|
rulesFile.createNewFile();
|
||||||
|
FileUtils.writeFile(rulesFile, """
|
||||||
|
{
|
||||||
|
"allow": [
|
||||||
|
"index.html",
|
||||||
|
"css/*",
|
||||||
|
"private/info.php"
|
||||||
|
],
|
||||||
|
"deny": [
|
||||||
|
"private/*"
|
||||||
|
],
|
||||||
|
"auth": [
|
||||||
|
"private/*",
|
||||||
|
"admin/*"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load authentication and rules
|
||||||
|
uniqueSessionString = AuthManager.sha256(new RandomString(new Random(System.currentTimeMillis()).nextInt(10, 20)).nextString());
|
||||||
|
|
||||||
|
AuthManager.loadAuthFile(authFile);
|
||||||
|
RuleManager.loadRules(rulesFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
public final File getContentFolder() {
|
||||||
|
return contentFolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final File getErrorsFolder() {
|
||||||
|
return errorsFolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final String getUniqueSessionString() {
|
||||||
|
return uniqueSessionString;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final int getSessionExpire() {
|
||||||
|
return sessionExpire;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final int getMaxUploadSize() {
|
||||||
|
return maxUploadSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the server receives a WebRequestPacket from the client.
|
||||||
|
*
|
||||||
|
* @param client The connected web client (pipeline + web socket).
|
||||||
|
* @param request The full decoded request packet.
|
||||||
|
* @return The response packet that should be sent back to the client.
|
||||||
|
*/
|
||||||
|
@Deprecated(forRemoval = false, since = "1.0.1-BETA.0.1")
|
||||||
|
public abstract WebResponsePacket onWebRequest(CustomConnectedClient client, WebRequestPacket request);
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
package org.openautonomousconnection.protocol.versions.v1_0_0.beta;
|
package org.openautonomousconnection.protocol.versions.v1_0_0.beta;
|
||||||
|
|
||||||
public enum WebRequestMethod {
|
public enum WebRequestMethod {
|
||||||
GET, POST
|
GET, POST, EXECUTE, SCRIPT
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import java.io.Serializable;
|
|||||||
/**
|
/**
|
||||||
* Enum representing the protocol versions for the Classic protocol.
|
* Enum representing the protocol versions for the Classic protocol.
|
||||||
*/
|
*/
|
||||||
@Deprecated(forRemoval = false, since = "1.0.0-BETA.3")
|
@Deprecated(forRemoval = true, since = "1.0.1-BETA.0.1")
|
||||||
public enum Classic_ProtocolVersion implements Serializable {
|
public enum Classic_ProtocolVersion implements Serializable {
|
||||||
PV_1_0_0("1.0.0");
|
PV_1_0_0("1.0.0");
|
||||||
public final String version;
|
public final String version;
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package org.openautonomousconnection.protocol.versions.v1_0_1.beta;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache behavior for navigation/resource requests.
|
||||||
|
*/
|
||||||
|
public enum WebCacheMode {
|
||||||
|
DEFAULT,
|
||||||
|
BYPASS,
|
||||||
|
ONLY_CACHE
|
||||||
|
}
|
||||||
@@ -0,0 +1,250 @@
|
|||||||
|
package org.openautonomousconnection.protocol.versions.v1_0_1.beta;
|
||||||
|
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory for creating {@link WebPacketHeader} instances with stable tab/page identifiers and
|
||||||
|
* monotonic request identifiers.
|
||||||
|
*
|
||||||
|
* <p>This class is intended to be used by the WebClient/WebServer integration layer to ensure
|
||||||
|
* every packet carries a valid header and consistent correlation metadata.</p>
|
||||||
|
*
|
||||||
|
* <p>Thread-safety:
|
||||||
|
* <ul>
|
||||||
|
* <li>Request id generation is thread-safe.</li>
|
||||||
|
* <li>Tab/page allocation methods are thread-safe.</li>
|
||||||
|
* <li>{@link WebTabContext} is thread-safe for reads; writes are synchronized.</li>
|
||||||
|
* </ul>
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public final class WebHeaderFactory {
|
||||||
|
|
||||||
|
private static final long MIN_ID = 1L;
|
||||||
|
|
||||||
|
private final AtomicLong requestIdSeq;
|
||||||
|
private final AtomicLong tabIdSeq;
|
||||||
|
private final AtomicLong pageIdSeq;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a factory with randomized id bases to reduce collisions across process restarts.
|
||||||
|
*/
|
||||||
|
public WebHeaderFactory() {
|
||||||
|
SecureRandom rnd = new SecureRandom();
|
||||||
|
this.requestIdSeq = new AtomicLong(Math.max(MIN_ID, rnd.nextLong() & Long.MAX_VALUE));
|
||||||
|
this.tabIdSeq = new AtomicLong(Math.max(MIN_ID, rnd.nextLong() & Long.MAX_VALUE));
|
||||||
|
this.pageIdSeq = new AtomicLong(Math.max(MIN_ID, rnd.nextLong() & Long.MAX_VALUE));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new tab context with a fresh tab id and an initial page id.
|
||||||
|
*
|
||||||
|
* @return new tab context
|
||||||
|
*/
|
||||||
|
public WebTabContext createTab() {
|
||||||
|
long tabId = nextTabId();
|
||||||
|
long pageId = nextPageId();
|
||||||
|
return new WebTabContext(tabId, pageId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts a new navigation by allocating a new page id for the given tab context.
|
||||||
|
*
|
||||||
|
* <p>Call this when navigation is initiated (typed URL, link click, reload, back/forward).</p>
|
||||||
|
*
|
||||||
|
* @param tab tab context
|
||||||
|
* @return the new page id
|
||||||
|
*/
|
||||||
|
public long beginNavigation(WebTabContext tab) {
|
||||||
|
Objects.requireNonNull(tab, "tab");
|
||||||
|
long newPageId = nextPageId();
|
||||||
|
tab.setPageId(newPageId);
|
||||||
|
return newPageId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a header for a navigation request.
|
||||||
|
*
|
||||||
|
* @param tab tab context
|
||||||
|
* @param noStore if true, sets {@link WebPacketFlags#NO_STORE}
|
||||||
|
* @return header
|
||||||
|
*/
|
||||||
|
public WebPacketHeader navigation(WebTabContext tab, boolean noStore) {
|
||||||
|
Objects.requireNonNull(tab, "tab");
|
||||||
|
int flags = WebPacketFlags.NAVIGATION;
|
||||||
|
if (noStore) flags |= WebPacketFlags.NO_STORE;
|
||||||
|
return header(nextRequestId(), tab.getTabId(), tab.getPageId(), 0L, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a header for a resource request.
|
||||||
|
*
|
||||||
|
* @param tab tab context
|
||||||
|
* @param frameId frame id (0 = main)
|
||||||
|
* @param noStore if true, sets {@link WebPacketFlags#NO_STORE}
|
||||||
|
* @return header
|
||||||
|
*/
|
||||||
|
public WebPacketHeader resource(WebTabContext tab, long frameId, boolean noStore) {
|
||||||
|
Objects.requireNonNull(tab, "tab");
|
||||||
|
int flags = WebPacketFlags.RESOURCE;
|
||||||
|
if (noStore) flags |= WebPacketFlags.NO_STORE;
|
||||||
|
return header(nextRequestId(), tab.getTabId(), tab.getPageId(), frameId, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a header for a resource response correlated to a request id.
|
||||||
|
*
|
||||||
|
* @param requestId request id to correlate
|
||||||
|
* @param tabId tab id
|
||||||
|
* @param pageId page id
|
||||||
|
* @param frameId frame id
|
||||||
|
* @return header
|
||||||
|
*/
|
||||||
|
public WebPacketHeader resourceResponse(long requestId, long tabId, long pageId, long frameId) {
|
||||||
|
return header(requestId, tabId, pageId, frameId, WebPacketFlags.RESOURCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a header for a streamed payload associated with a request id.
|
||||||
|
*
|
||||||
|
* @param requestId correlated request id
|
||||||
|
* @param tabId tab id
|
||||||
|
* @param pageId page id
|
||||||
|
* @param frameId frame id
|
||||||
|
* @return header
|
||||||
|
*/
|
||||||
|
public WebPacketHeader stream(long requestId, long tabId, long pageId, long frameId) {
|
||||||
|
return header(requestId, tabId, pageId, frameId, WebPacketFlags.STREAM);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an event header (server->client or client->server).
|
||||||
|
*
|
||||||
|
* @param tabId tab id
|
||||||
|
* @param pageId page id
|
||||||
|
* @param frameId frame id
|
||||||
|
* @return header
|
||||||
|
*/
|
||||||
|
public WebPacketHeader event(long tabId, long pageId, long frameId) {
|
||||||
|
return header(nextRequestId(), tabId, pageId, frameId, WebPacketFlags.EVENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an event header correlated to an existing request id.
|
||||||
|
*
|
||||||
|
* @param requestId request id
|
||||||
|
* @param tabId tab id
|
||||||
|
* @param pageId page id
|
||||||
|
* @param frameId frame id
|
||||||
|
* @return header
|
||||||
|
*/
|
||||||
|
public WebPacketHeader correlatedEvent(long requestId, long tabId, long pageId, long frameId) {
|
||||||
|
return header(requestId, tabId, pageId, frameId, WebPacketFlags.EVENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a header for a document snapshot event.
|
||||||
|
*
|
||||||
|
* @param tab tab context
|
||||||
|
* @param frameId frame id
|
||||||
|
* @return header
|
||||||
|
*/
|
||||||
|
public WebPacketHeader documentSnapshotEvent(WebTabContext tab, long frameId) {
|
||||||
|
Objects.requireNonNull(tab, "tab");
|
||||||
|
return header(nextRequestId(), tab.getTabId(), tab.getPageId(), frameId, WebPacketFlags.EVENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a header for a document apply request.
|
||||||
|
*
|
||||||
|
* @param tab tab context
|
||||||
|
* @param frameId frame id
|
||||||
|
* @return header
|
||||||
|
*/
|
||||||
|
public WebPacketHeader documentApply(WebTabContext tab, long frameId) {
|
||||||
|
Objects.requireNonNull(tab, "tab");
|
||||||
|
return header(nextRequestId(), tab.getTabId(), tab.getPageId(), frameId, WebPacketFlags.RESOURCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether an incoming packet is stale for a given tab context.
|
||||||
|
*
|
||||||
|
* <p>Stale means: the packet refers to an older pageId than the current tab pageId.</p>
|
||||||
|
*
|
||||||
|
* @param tab tab context
|
||||||
|
* @param incoming incoming header
|
||||||
|
* @return true if stale, false otherwise
|
||||||
|
*/
|
||||||
|
public boolean isStale(WebTabContext tab, WebPacketHeader incoming) {
|
||||||
|
Objects.requireNonNull(tab, "tab");
|
||||||
|
Objects.requireNonNull(incoming, "incoming");
|
||||||
|
return incoming.getTabId() == tab.getTabId() && incoming.getPageId() != tab.getPageId();
|
||||||
|
}
|
||||||
|
|
||||||
|
private WebPacketHeader header(long requestId, long tabId, long pageId, long frameId, int flags) {
|
||||||
|
long ts = System.currentTimeMillis();
|
||||||
|
return new WebPacketHeader(requestId, tabId, pageId, frameId, flags, ts);
|
||||||
|
}
|
||||||
|
|
||||||
|
private long nextRequestId() {
|
||||||
|
long v = requestIdSeq.incrementAndGet();
|
||||||
|
return (v < MIN_ID) ? MIN_ID : v;
|
||||||
|
}
|
||||||
|
|
||||||
|
private long nextTabId() {
|
||||||
|
long v = tabIdSeq.incrementAndGet();
|
||||||
|
return (v < MIN_ID) ? MIN_ID : v;
|
||||||
|
}
|
||||||
|
|
||||||
|
private long nextPageId() {
|
||||||
|
long v = pageIdSeq.incrementAndGet();
|
||||||
|
return (v < MIN_ID) ? MIN_ID : v;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents per-tab state used for header creation and stale detection.
|
||||||
|
*/
|
||||||
|
public static final class WebTabContext {
|
||||||
|
|
||||||
|
private final long tabId;
|
||||||
|
private volatile long pageId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a tab context.
|
||||||
|
*
|
||||||
|
* @param tabId tab id
|
||||||
|
* @param pageId initial page id
|
||||||
|
*/
|
||||||
|
public WebTabContext(long tabId, long pageId) {
|
||||||
|
if (tabId < MIN_ID) throw new IllegalArgumentException("tabId must be >= 1");
|
||||||
|
if (pageId < MIN_ID) throw new IllegalArgumentException("pageId must be >= 1");
|
||||||
|
this.tabId = tabId;
|
||||||
|
this.pageId = pageId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return stable tab id
|
||||||
|
*/
|
||||||
|
public long getTabId() {
|
||||||
|
return tabId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return current page id
|
||||||
|
*/
|
||||||
|
public long getPageId() {
|
||||||
|
return pageId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the current page id.
|
||||||
|
*
|
||||||
|
* @param pageId new page id
|
||||||
|
*/
|
||||||
|
public synchronized void setPageId(long pageId) {
|
||||||
|
if (pageId < MIN_ID) throw new IllegalArgumentException("pageId must be >= 1");
|
||||||
|
this.pageId = pageId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package org.openautonomousconnection.protocol.versions.v1_0_1.beta;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identifies the initiator of a resource request.
|
||||||
|
*/
|
||||||
|
public enum WebInitiatorType {
|
||||||
|
DOCUMENT,
|
||||||
|
SCRIPT,
|
||||||
|
STYLE,
|
||||||
|
IMAGE,
|
||||||
|
MEDIA,
|
||||||
|
FONT,
|
||||||
|
XHR,
|
||||||
|
FETCH,
|
||||||
|
OTHER
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package org.openautonomousconnection.protocol.versions.v1_0_1.beta;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bitset flags used in WebPacketHeader.
|
||||||
|
*/
|
||||||
|
public final class WebPacketFlags {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Packet describes a top-level navigation action.
|
||||||
|
*/
|
||||||
|
public static final int NAVIGATION = 1 << 0;
|
||||||
|
/**
|
||||||
|
* Packet describes a resource request/response.
|
||||||
|
*/
|
||||||
|
public static final int RESOURCE = 1 << 1;
|
||||||
|
/**
|
||||||
|
* Packet is part of a streamed body transfer.
|
||||||
|
*/
|
||||||
|
public static final int STREAM = 1 << 2;
|
||||||
|
/**
|
||||||
|
* Packet is devtools-related.
|
||||||
|
*/
|
||||||
|
public static final int DEVTOOLS = 1 << 3;
|
||||||
|
/**
|
||||||
|
* Packet is an event (server->client / client->server).
|
||||||
|
*/
|
||||||
|
public static final int EVENT = 1 << 4;
|
||||||
|
/**
|
||||||
|
* Disable caching / do not store.
|
||||||
|
*/
|
||||||
|
public static final int NO_STORE = 1 << 5;
|
||||||
|
|
||||||
|
private WebPacketFlags() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a flag is present.
|
||||||
|
*
|
||||||
|
* @param flags bitset
|
||||||
|
* @param flag flag constant
|
||||||
|
* @return true if present
|
||||||
|
*/
|
||||||
|
public static boolean has(int flags, int flag) {
|
||||||
|
return (flags & flag) == flag;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
package org.openautonomousconnection.protocol.versions.v1_0_1.beta;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shared header for all Web v1.0.1-BETA packets.
|
||||||
|
*
|
||||||
|
* <p>This header MUST be written/read first in every web packet.</p>
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
public final class WebPacketHeader {
|
||||||
|
|
||||||
|
private long requestId;
|
||||||
|
private long tabId;
|
||||||
|
private long pageId;
|
||||||
|
private long frameId;
|
||||||
|
private int flags;
|
||||||
|
private long timestampMs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Empty constructor for decoding.
|
||||||
|
*/
|
||||||
|
public WebPacketHeader() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new web header.
|
||||||
|
*
|
||||||
|
* @param requestId correlation id (request/response/stream/events)
|
||||||
|
* @param tabId stable tab id
|
||||||
|
* @param pageId navigation instance id
|
||||||
|
* @param frameId frame id (0 = main frame)
|
||||||
|
* @param flags bitset flags
|
||||||
|
* @param timestampMs unix epoch millis (0 allowed)
|
||||||
|
*/
|
||||||
|
public WebPacketHeader(long requestId, long tabId, long pageId, long frameId, int flags, long timestampMs) {
|
||||||
|
this.requestId = requestId;
|
||||||
|
this.tabId = tabId;
|
||||||
|
this.pageId = pageId;
|
||||||
|
this.frameId = frameId;
|
||||||
|
this.flags = flags;
|
||||||
|
this.timestampMs = timestampMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads a header from the stream.
|
||||||
|
*
|
||||||
|
* @param in stream
|
||||||
|
* @return decoded header
|
||||||
|
* @throws IOException on IO error
|
||||||
|
*/
|
||||||
|
public static WebPacketHeader read(DataInputStream in) throws IOException {
|
||||||
|
WebPacketHeader h = new WebPacketHeader();
|
||||||
|
h.requestId = in.readLong();
|
||||||
|
h.tabId = in.readLong();
|
||||||
|
h.pageId = in.readLong();
|
||||||
|
h.frameId = in.readLong();
|
||||||
|
h.flags = in.readInt();
|
||||||
|
h.timestampMs = in.readLong();
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes the header to the stream.
|
||||||
|
*
|
||||||
|
* @param out stream
|
||||||
|
* @throws IOException on IO error
|
||||||
|
*/
|
||||||
|
public void write(DataOutputStream out) throws IOException {
|
||||||
|
out.writeLong(requestId);
|
||||||
|
out.writeLong(tabId);
|
||||||
|
out.writeLong(pageId);
|
||||||
|
out.writeLong(frameId);
|
||||||
|
out.writeInt(flags);
|
||||||
|
out.writeLong(timestampMs);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package org.openautonomousconnection.protocol.versions.v1_0_1.beta;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes how a navigation was triggered.
|
||||||
|
*/
|
||||||
|
public enum WebTransitionType {
|
||||||
|
LINK,
|
||||||
|
TYPED,
|
||||||
|
RELOAD,
|
||||||
|
BACK_FORWARD
|
||||||
|
}
|
||||||
@@ -0,0 +1,281 @@
|
|||||||
|
package org.openautonomousconnection.protocol.versions.v1_0_1.beta.compat;
|
||||||
|
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.WebRequestPacket;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.WebResponsePacket;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.stream.WebStreamChunkPacket_v1_0_0_B;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.stream.WebStreamEndPacket_v1_0_0_B;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.stream.WebStreamStartPacket_v1_0_0_B;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.resource.WebResourceRequestPacket;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.resource.WebResourceResponsePacket;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.stream.WebStreamChunkPacket_v1_0_1_B;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.stream.WebStreamEndPacket_v1_0_1_B;
|
||||||
|
import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.stream.WebStreamStartPacket_v1_0_1_B;
|
||||||
|
import org.openautonomousconnection.protocol.versions.v1_0_0.beta.WebRequestMethod;
|
||||||
|
import org.openautonomousconnection.protocol.versions.v1_0_1.beta.WebCacheMode;
|
||||||
|
import org.openautonomousconnection.protocol.versions.v1_0_1.beta.WebInitiatorType;
|
||||||
|
import org.openautonomousconnection.protocol.versions.v1_0_1.beta.WebPacketFlags;
|
||||||
|
import org.openautonomousconnection.protocol.versions.v1_0_1.beta.WebPacketHeader;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compatibility mapper between Web protocol v1.0.0
|
||||||
|
* and Web protocol v1.0.1 (beta).
|
||||||
|
*
|
||||||
|
* <p>This class provides pure mapping logic and contains no networking code.</p>
|
||||||
|
*
|
||||||
|
* <p>Usage:
|
||||||
|
* <ul>
|
||||||
|
* <li>Server-side: map 1.0.0 {@link WebRequestPacket} to {@link WebResourceRequestPacket}</li>
|
||||||
|
* <li>Server-side: map {@link WebResourceResponsePacket} back to {@link WebResponsePacket}</li>
|
||||||
|
* <li>Client-side: map 1.0.0 responses/streams to v1.0.1 packets so v1.0.1 hooks can be reused</li>
|
||||||
|
* </ul>
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public final class WebCompatMapper {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Synthetic request id generator for 1.0.0 requests.
|
||||||
|
*/
|
||||||
|
private static final AtomicLong COMPAT_REQUEST_ID = new AtomicLong(1L);
|
||||||
|
|
||||||
|
private WebCompatMapper() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a monotonic synthetic request id.
|
||||||
|
*
|
||||||
|
* @return next request id
|
||||||
|
*/
|
||||||
|
public static long nextCompatRequestId() {
|
||||||
|
return COMPAT_REQUEST_ID.getAndIncrement();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps a v1.0.0 {@link WebRequestPacket} to a v1.0.1 {@link WebResourceRequestPacket}.
|
||||||
|
*
|
||||||
|
* <p>Because v1.0.0 has no tab/page/frame/request correlation model,
|
||||||
|
* synthetic identifiers must be provided.</p>
|
||||||
|
*
|
||||||
|
* @param requestId synthetic request id
|
||||||
|
* @param tabId synthetic tab id
|
||||||
|
* @param pageId synthetic page id
|
||||||
|
* @param requestPacket 1.0.0 request
|
||||||
|
* @return mapped v1.0.1 resource request
|
||||||
|
*/
|
||||||
|
public static WebResourceRequestPacket toResourceRequest(
|
||||||
|
long requestId,
|
||||||
|
long tabId,
|
||||||
|
long pageId,
|
||||||
|
WebRequestPacket requestPacket
|
||||||
|
) {
|
||||||
|
Objects.requireNonNull(requestPacket, "requestPacket");
|
||||||
|
|
||||||
|
String path = requestPacket.getPath();
|
||||||
|
String method = mapMethod(requestPacket.getMethod());
|
||||||
|
Map<String, String> headers = requestPacket.getHeaders() != null ? requestPacket.getHeaders() : Collections.emptyMap();
|
||||||
|
byte[] body = requestPacket.getBody() != null ? requestPacket.getBody() : new byte[0];
|
||||||
|
|
||||||
|
WebPacketHeader header = new WebPacketHeader(
|
||||||
|
requestId,
|
||||||
|
tabId,
|
||||||
|
pageId,
|
||||||
|
0L,
|
||||||
|
WebPacketFlags.RESOURCE,
|
||||||
|
System.currentTimeMillis()
|
||||||
|
);
|
||||||
|
|
||||||
|
return new WebResourceRequestPacket(
|
||||||
|
header,
|
||||||
|
path,
|
||||||
|
method,
|
||||||
|
headers,
|
||||||
|
body,
|
||||||
|
null,
|
||||||
|
WebInitiatorType.OTHER,
|
||||||
|
WebCacheMode.DEFAULT
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps a v1.0.1 {@link WebResourceResponsePacket} back to a 1.0.0 {@link WebResponsePacket}.
|
||||||
|
*
|
||||||
|
* <p>Correlation information is lost because v1.0.0 does not support it.</p>
|
||||||
|
*
|
||||||
|
* @param response v1.0.1 resource response
|
||||||
|
* @return 1.0.0 response packet
|
||||||
|
*/
|
||||||
|
public static WebResponsePacket to100BResponse(WebResourceResponsePacket response) {
|
||||||
|
Objects.requireNonNull(response, "response");
|
||||||
|
|
||||||
|
return new WebResponsePacket(
|
||||||
|
response.getStatusCode(),
|
||||||
|
response.getContentType(),
|
||||||
|
response.getHeaders(),
|
||||||
|
response.getBody()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps v1.0.1 resource response to 1.0.0 response with overridden body.
|
||||||
|
*
|
||||||
|
* <p>Useful when buffering streamed responses for 1.0.0 clients.</p>
|
||||||
|
*
|
||||||
|
* @param response original v1.0.1 response
|
||||||
|
* @param body buffered full body
|
||||||
|
* @return 1.0.0 response
|
||||||
|
*/
|
||||||
|
public static WebResponsePacket to100BResponse(WebResourceResponsePacket response, byte[] body) {
|
||||||
|
Objects.requireNonNull(response, "response");
|
||||||
|
|
||||||
|
return new WebResponsePacket(
|
||||||
|
response.getStatusCode(),
|
||||||
|
response.getContentType(),
|
||||||
|
response.getHeaders(),
|
||||||
|
body != null ? body : new byte[0]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps a v1.0.0 {@link WebResponsePacket} to a v1.0.1 {@link WebResourceResponsePacket}
|
||||||
|
* using the provided synthetic correlation header.
|
||||||
|
*
|
||||||
|
* @param correlationHeader synthetic correlation header (requestId/tabId/pageId/frameId)
|
||||||
|
* @param responsePacket 1.0.0 response packet
|
||||||
|
* @return v1.0.1 resource response packet
|
||||||
|
*/
|
||||||
|
public static WebResourceResponsePacket toV101ResourceResponse(WebPacketHeader correlationHeader, WebResponsePacket responsePacket) {
|
||||||
|
Objects.requireNonNull(correlationHeader, "correlationHeader");
|
||||||
|
Objects.requireNonNull(responsePacket, "responsePacket");
|
||||||
|
|
||||||
|
WebPacketHeader header = mirrorCorrelation(correlationHeader, WebPacketFlags.RESOURCE);
|
||||||
|
|
||||||
|
return new WebResourceResponsePacket(
|
||||||
|
header,
|
||||||
|
responsePacket.getStatusCode(),
|
||||||
|
responsePacket.getContentType(),
|
||||||
|
responsePacket.getHeaders(),
|
||||||
|
responsePacket.getBody(),
|
||||||
|
null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps a v1.0.0 {@link WebStreamStartPacket_v1_0_0_B} to a v1.0.1 {@link WebStreamStartPacket_v1_0_1_B}
|
||||||
|
* using the provided synthetic correlation header.
|
||||||
|
*
|
||||||
|
* @param correlationHeader synthetic correlation header
|
||||||
|
* @param streamPacket 1.0.0 stream start
|
||||||
|
* @return v1.0.1 stream start
|
||||||
|
*/
|
||||||
|
public static WebStreamStartPacket_v1_0_1_B toV101StreamStart(WebPacketHeader correlationHeader, WebStreamStartPacket_v1_0_0_B streamPacket) {
|
||||||
|
Objects.requireNonNull(correlationHeader, "correlationHeader");
|
||||||
|
Objects.requireNonNull(streamPacket, "streamPacket");
|
||||||
|
|
||||||
|
WebPacketHeader header = mirrorCorrelation(correlationHeader, WebPacketFlags.RESOURCE);
|
||||||
|
|
||||||
|
return new WebStreamStartPacket_v1_0_1_B(
|
||||||
|
header,
|
||||||
|
streamPacket.getStatusCode(),
|
||||||
|
streamPacket.getContentType(),
|
||||||
|
streamPacket.getHeaders(),
|
||||||
|
streamPacket.getTotalLength()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps a v1.0.0 {@link WebStreamChunkPacket_v1_0_0_B} to a v1.0.1 {@link WebStreamChunkPacket_v1_0_1_B}
|
||||||
|
* using the provided synthetic correlation header.
|
||||||
|
*
|
||||||
|
* @param correlationHeader synthetic correlation header
|
||||||
|
* @param streamPacket 1.0.0 stream chunk
|
||||||
|
* @return v1.0.1 stream chunk
|
||||||
|
*/
|
||||||
|
public static WebStreamChunkPacket_v1_0_1_B toV101StreamChunk(WebPacketHeader correlationHeader, WebStreamChunkPacket_v1_0_0_B streamPacket) {
|
||||||
|
Objects.requireNonNull(correlationHeader, "correlationHeader");
|
||||||
|
Objects.requireNonNull(streamPacket, "streamPacket");
|
||||||
|
|
||||||
|
WebPacketHeader header = mirrorCorrelation(correlationHeader, WebPacketFlags.RESOURCE);
|
||||||
|
|
||||||
|
return new WebStreamChunkPacket_v1_0_1_B(
|
||||||
|
header,
|
||||||
|
streamPacket.getSeq(),
|
||||||
|
streamPacket.getData()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps a v1.0.0 {@link WebStreamEndPacket_v1_0_0_B} to a v1.0.1 {@link WebStreamEndPacket_v1_0_1_B}
|
||||||
|
* using the provided synthetic correlation header.
|
||||||
|
*
|
||||||
|
* @param correlationHeader synthetic correlation header
|
||||||
|
* @param streamPacket v1.0.0 stream end packet
|
||||||
|
* @return v1.0.1 stream end packet
|
||||||
|
*/
|
||||||
|
public static WebStreamEndPacket_v1_0_1_B toV101StreamEnd(
|
||||||
|
WebPacketHeader correlationHeader,
|
||||||
|
WebStreamEndPacket_v1_0_0_B streamPacket
|
||||||
|
) {
|
||||||
|
Objects.requireNonNull(correlationHeader, "correlationHeader");
|
||||||
|
Objects.requireNonNull(streamPacket, "streamPacket");
|
||||||
|
|
||||||
|
WebPacketHeader header = mirrorCorrelation(correlationHeader, WebPacketFlags.RESOURCE);
|
||||||
|
|
||||||
|
// v1.0.0 has no error string -> null
|
||||||
|
return new WebStreamEndPacket_v1_0_1_B(
|
||||||
|
header,
|
||||||
|
streamPacket.isOk(),
|
||||||
|
null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a header that mirrors correlation fields from the provided header and updates timestamp/flags.
|
||||||
|
*
|
||||||
|
* @param correlationHeader correlation header to mirror
|
||||||
|
* @param extraFlags flags to OR
|
||||||
|
* @return mirrored header
|
||||||
|
*/
|
||||||
|
public static WebPacketHeader mirrorCorrelation(WebPacketHeader correlationHeader, int extraFlags) {
|
||||||
|
Objects.requireNonNull(correlationHeader, "correlationHeader");
|
||||||
|
return new WebPacketHeader(
|
||||||
|
correlationHeader.getRequestId(),
|
||||||
|
correlationHeader.getTabId(),
|
||||||
|
correlationHeader.getPageId(),
|
||||||
|
correlationHeader.getFrameId(),
|
||||||
|
correlationHeader.getFlags() | extraFlags,
|
||||||
|
System.currentTimeMillis()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps 1.0.0 {@link WebRequestMethod}.
|
||||||
|
*
|
||||||
|
* @param method 1.0.0 method
|
||||||
|
* @return method string
|
||||||
|
*/
|
||||||
|
public static String mapMethod(WebRequestMethod method) {
|
||||||
|
if (method == null) return "GET";
|
||||||
|
return switch (method) {
|
||||||
|
case GET -> "GET";
|
||||||
|
case POST, EXECUTE, SCRIPT -> "POST";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps 1.0.1 to 1.0.0 {@link WebRequestMethod}.
|
||||||
|
*
|
||||||
|
* @param method 1.0.0 method
|
||||||
|
* @return method string
|
||||||
|
*/
|
||||||
|
public static WebRequestMethod map100BMethod(String method) {
|
||||||
|
if (method == null) return WebRequestMethod.GET;
|
||||||
|
return switch (method.toUpperCase()) {
|
||||||
|
case "POST" -> WebRequestMethod.POST;
|
||||||
|
default -> WebRequestMethod.GET;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user