This commit is contained in:
UnlegitDqrk
2026-02-27 20:30:24 +01:00
parent d5b880d70c
commit 8ef2f0291f
23 changed files with 638 additions and 190 deletions

View File

@@ -6,7 +6,7 @@
<groupId>org.openautonomousconnection</groupId> <groupId>org.openautonomousconnection</groupId>
<artifactId>Protocol</artifactId> <artifactId>Protocol</artifactId>
<version>1.0.1-BETA.0.4</version> <version>1.0.1-BETA.0.5</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,7 +84,7 @@
<dependency> <dependency>
<groupId>dev.unlegitdqrk</groupId> <groupId>dev.unlegitdqrk</groupId>
<artifactId>unlegitlibrary</artifactId> <artifactId>unlegitlibrary</artifactId>
<version>1.8.2</version> <version>1.8.3</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.projectlombok</groupId> <groupId>org.projectlombok</groupId>

View File

@@ -40,6 +40,7 @@ import org.openautonomousconnection.protocol.versions.v1_0_0.beta.ProtocolWebSer
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.ResourceBundle;
import java.util.function.Supplier; import java.util.function.Supplier;
/** /**
@@ -53,18 +54,6 @@ public final class ProtocolBridge {
@Getter @Getter
private final ProtocolValues protocolValues; private final ProtocolValues protocolValues;
/**
* The protocol version for the current connection
*/
@Getter
private final ProtocolVersion protocolVersion;
/**
* The logger instance for logging events and errors
*/
@Getter
private Logger logger;
/** /**
* The protocol side instances * The protocol side instances
*/ */
@@ -78,27 +67,17 @@ public final class ProtocolBridge {
@Getter @Getter
private ProtocolCustomServer protocolServer; private ProtocolCustomServer protocolServer;
@Getter
private AddonLoader addonLoader;
/** /**
* Initialize the ProtocolBridge instance for the client side * Initialize the ProtocolBridge instance for the client side
* *
* @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 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, public ProtocolBridge(ProtocolCustomServer protocolServer, ProtocolValues protocolValues) throws Exception {
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.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);
@@ -118,20 +97,14 @@ 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 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, public ProtocolBridge(ProtocolClient protocolClient, LibClientImpl_v1_0_1_B libClientImpl,
Logger logger, AddonLoader addonLoader, LibClientImpl_v1_0_1_B libClientImpl) throws Exception { ProtocolValues protocolValues) 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.logger = logger;
this.addonLoader = addonLoader;
protocolClient.attachBridge(this); protocolClient.attachBridge(this);
initializeProtocolVersion(); initializeProtocolVersion();
@@ -145,11 +118,11 @@ public final class ProtocolBridge {
} }
private void installUrl(LibClientImpl_v1_0_1_B libClientImpl) { private void installUrl(LibClientImpl_v1_0_1_B libClientImpl) {
if (protocolVersion == ProtocolVersion.PV_1_0_0_BETA) { if (protocolValues.protocolVersion == ProtocolVersion.PV_1_0_0_BETA) {
OacWebUrlInstaller_v1_0_0_B.installOnce(this, libClientImpl); OacWebUrlInstaller_v1_0_0_B.installOnce(this, libClientImpl);
} }
if (protocolVersion == ProtocolVersion.PV_1_0_1_BETA) { if (protocolValues.protocolVersion == ProtocolVersion.PV_1_0_1_BETA) {
OacUrlHandlerInstaller_v1_0_1_B.installOnce(this, libClientImpl, libClientImpl, libClientImpl); OacUrlHandlerInstaller_v1_0_1_B.installOnce(this, libClientImpl, libClientImpl, libClientImpl);
} }
} }
@@ -211,12 +184,12 @@ public final class ProtocolBridge {
registerPacket(WebStreamEndPacket_v1_0_0_B::new); registerPacket(WebStreamEndPacket_v1_0_0_B::new);
// 1.0.1-BETA Packets // 1.0.1-BETA Packets
registerPacket(WebDocumentApplyRequestPacket::new); registerPacket(() -> new WebDocumentApplyRequestPacket(this));
registerPacket(WebDocumentApplyResponsePacket::new); registerPacket(WebDocumentApplyResponsePacket::new);
registerPacket(WebDocumentSnapshotEventPacket::new); registerPacket(WebDocumentSnapshotEventPacket::new);
registerPacket(WebNavigateRequestPacket::new); registerPacket(() -> new WebNavigateRequestPacket(this));
registerPacket(WebNavigateAckPacket::new); registerPacket(WebNavigateAckPacket::new);
registerPacket(WebResourceRequestPacket::new); registerPacket(() -> new WebResourceRequestPacket(this));
registerPacket(WebResourceResponsePacket::new); registerPacket(WebResourceResponsePacket::new);
registerPacket(WebStreamStartPacket_v1_0_1_B::new); registerPacket(WebStreamStartPacket_v1_0_1_B::new);
registerPacket(WebStreamChunkPacket_v1_0_1_B::new); registerPacket(WebStreamChunkPacket_v1_0_1_B::new);
@@ -255,7 +228,7 @@ public final class ProtocolBridge {
// Check if the protocol version is valid for the current side // Check if the protocol version is valid for the current side
// If not, log an error and exit the application // If not, log an error and exit the application
if (!validateProtocolSide()) { if (!validateProtocolSide()) {
this.logger.error("Invalid protocol version '" + protocolVersion.toString() + "'!"); protocolValues.logger.error("Invalid protocol version '" + protocolValues.protocolVersion.toString() + "'!");
System.exit(1); System.exit(1);
} }
} }
@@ -267,14 +240,14 @@ public final class ProtocolBridge {
*/ */
public boolean isClassicSupported() { public boolean isClassicSupported() {
boolean yes = false; boolean yes = false;
for (ProtocolVersion compatibleVersion : protocolVersion.getCompatibleVersions()) { for (ProtocolVersion compatibleVersion : protocolValues.protocolVersion.getCompatibleVersions()) {
// Check if the compatible version is classic // Check if the compatible version is classic
yes = compatibleVersion.getProtocolType() == ProtocolVersion.ProtocolType.CLASSIC; yes = compatibleVersion.getProtocolType() == ProtocolVersion.ProtocolType.CLASSIC;
if (yes) break; if (yes) break;
} }
// Check if the current protocol version is classic or if it is supported by any of the compatible versions // Check if the current protocol version is classic or if it is supported by any of the compatible versions
return protocolVersion.getProtocolType() == ProtocolVersion.ProtocolType.CLASSIC || yes; return protocolValues.protocolVersion.getProtocolType() == ProtocolVersion.ProtocolType.CLASSIC || yes;
} }
/** /**
@@ -286,14 +259,14 @@ 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 : protocolValues.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);
if (yes) break; if (yes) break;
} }
// Check if the current protocol version supports the target protocol or if it is supported by any of the compatible versions // Check if the current protocol version supports the target protocol or if it is supported by any of the compatible versions
return protocolVersion.getSupportedProtocols().contains(protocol) || yes; return protocolValues.protocolVersion.getSupportedProtocols().contains(protocol) || yes;
} }
/** /**
@@ -320,7 +293,7 @@ public final class ProtocolBridge {
*/ */
public boolean isVersionSupported(ProtocolVersion targetVersion) { public boolean isVersionSupported(ProtocolVersion targetVersion) {
// Check if the target protocol version is the same as the current protocol version or if it is in the list of compatible versions // Check if the target protocol version is the same as the current protocol version or if it is in the list of compatible versions
return protocolVersion == targetVersion || protocolVersion.getCompatibleVersions().contains(targetVersion); return protocolValues.protocolVersion == targetVersion || protocolValues.protocolVersion.getCompatibleVersions().contains(targetVersion);
} }
/** /**
@@ -330,22 +303,22 @@ public final class ProtocolBridge {
*/ */
private boolean validateProtocolSide() { private boolean validateProtocolSide() {
return return
(isRunningAsClient() && protocolVersion.getProtocolSide() == ProtocolVersion.ProtocolSide.CLIENT) || (isRunningAsClient() && protocolValues.protocolVersion.getProtocolSide() == ProtocolVersion.ProtocolSide.CLIENT) ||
(isRunningAsClient() && protocolVersion.getProtocolSide() == ProtocolVersion.ProtocolSide.CLIENT_WEB) || (isRunningAsClient() && protocolValues.protocolVersion.getProtocolSide() == ProtocolVersion.ProtocolSide.CLIENT_WEB) ||
(isRunningAsClient() && protocolVersion.getProtocolSide() == ProtocolVersion.ProtocolSide.CLIENT_INS) || (isRunningAsClient() && protocolValues.protocolVersion.getProtocolSide() == ProtocolVersion.ProtocolSide.CLIENT_INS) ||
(isRunningAsClient() && protocolVersion.getProtocolSide() == ProtocolVersion.ProtocolSide.ALL) || (isRunningAsClient() && protocolValues.protocolVersion.getProtocolSide() == ProtocolVersion.ProtocolSide.ALL) ||
(isRunningAsWebServer() && protocolVersion.getProtocolSide() == ProtocolVersion.ProtocolSide.WEB) || (isRunningAsWebServer() && protocolValues.protocolVersion.getProtocolSide() == ProtocolVersion.ProtocolSide.WEB) ||
(isRunningAsWebServer() && protocolVersion.getProtocolSide() == ProtocolVersion.ProtocolSide.CLIENT_WEB) || (isRunningAsWebServer() && protocolValues.protocolVersion.getProtocolSide() == ProtocolVersion.ProtocolSide.CLIENT_WEB) ||
(isRunningAsWebServer() && protocolVersion.getProtocolSide() == ProtocolVersion.ProtocolSide.WEB_INS) || (isRunningAsWebServer() && protocolValues.protocolVersion.getProtocolSide() == ProtocolVersion.ProtocolSide.WEB_INS) ||
(isRunningAsWebServer() && protocolVersion.getProtocolSide() == ProtocolVersion.ProtocolSide.ALL) || (isRunningAsWebServer() && protocolValues.protocolVersion.getProtocolSide() == ProtocolVersion.ProtocolSide.ALL) ||
(isRunningAsServer() && protocolVersion.getProtocolSide() == ProtocolVersion.ProtocolSide.ALL) || (isRunningAsServer() && protocolValues.protocolVersion.getProtocolSide() == ProtocolVersion.ProtocolSide.ALL) ||
(isRunningAsINSServer() && protocolVersion.getProtocolSide() == ProtocolVersion.ProtocolSide.INS) || (isRunningAsINSServer() && protocolValues.protocolVersion.getProtocolSide() == ProtocolVersion.ProtocolSide.INS) ||
(isRunningAsINSServer() && protocolVersion.getProtocolSide() == ProtocolVersion.ProtocolSide.WEB_INS) || (isRunningAsINSServer() && protocolValues.protocolVersion.getProtocolSide() == ProtocolVersion.ProtocolSide.WEB_INS) ||
(isRunningAsINSServer() && protocolVersion.getProtocolSide() == ProtocolVersion.ProtocolSide.CLIENT_INS) || (isRunningAsINSServer() && protocolValues.protocolVersion.getProtocolSide() == ProtocolVersion.ProtocolSide.CLIENT_INS) ||
(isRunningAsINSServer() && protocolVersion.getProtocolSide() == ProtocolVersion.ProtocolSide.ALL); (isRunningAsINSServer() && protocolValues.protocolVersion.getProtocolSide() == ProtocolVersion.ProtocolSide.ALL);
} }
/** /**
@@ -372,6 +345,8 @@ 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() {
if (protocolValues.protocolVersion == ProtocolVersion.PV_1_0_0_BETA)
return isRunningAsServer() && protocolServer instanceof ProtocolWebServer_1_0_0_B;
return isRunningAsServer() && protocolServer instanceof ProtocolWebServer; return isRunningAsServer() && protocolServer instanceof ProtocolWebServer;
} }

View File

@@ -1,9 +1,12 @@
package org.openautonomousconnection.protocol; package org.openautonomousconnection.protocol;
import dev.unlegitdqrk.unlegitlibrary.addon.AddonLoader;
import dev.unlegitdqrk.unlegitlibrary.event.EventManager; import dev.unlegitdqrk.unlegitlibrary.event.EventManager;
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.PacketHandler; import dev.unlegitdqrk.unlegitlibrary.network.system.packets.PacketHandler;
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.ClientAuthMode; import dev.unlegitdqrk.unlegitlibrary.network.system.utils.ClientAuthMode;
import dev.unlegitdqrk.unlegitlibrary.utils.DefaultMethodsOverrider; import dev.unlegitdqrk.unlegitlibrary.utils.DefaultMethodsOverrider;
import dev.unlegitdqrk.unlegitlibrary.utils.Logger;
import org.openautonomousconnection.protocol.versions.ProtocolVersion;
/** /**
* Settings for the protocol connection. * Settings for the protocol connection.
@@ -19,8 +22,14 @@ public final class ProtocolValues extends DefaultMethodsOverrider {
*/ */
public EventManager eventManager; public EventManager eventManager;
public AddonLoader addonLoader;
public Logger logger;
public String keyPass = null; public String keyPass = null;
public ProtocolVersion protocolVersion;
public boolean ssl = true; public boolean ssl = true;
public ClientAuthMode authMode = ClientAuthMode.NONE; public ClientAuthMode authMode = ClientAuthMode.NONE;

View File

@@ -8,6 +8,7 @@ import java.lang.annotation.RetentionPolicy;
/** /**
* Annotation to provide metadata about protocol handlers or classes. * Annotation to provide metadata about protocol handlers or classes.
*/ */
@Deprecated(forRemoval = false, since = "1.0.1-BETA.0.5")
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
public @interface ProtocolInfo { public @interface ProtocolInfo {

View File

@@ -43,7 +43,7 @@ public final class ClientListener extends EventListener {
try { try {
event.getClient().sendPacket(new AuthPacket(client.getProtocolBridge()), TransportProtocol.TCP); event.getClient().sendPacket(new AuthPacket(client.getProtocolBridge()), TransportProtocol.TCP);
} catch (Exception exception) { } catch (Exception exception) {
client.getProtocolBridge().getLogger().exception("Failed to send auth packet", exception); client.getProtocolBridge().getProtocolValues().logger.exception("Failed to send auth packet", exception);
event.getClient().disconnect(); event.getClient().disconnect();
} }
} }

View File

@@ -52,7 +52,7 @@ public final class CustomServerListener extends EventListener {
try { try {
server.getClients().add(new CustomConnectedClient(event.getClient(), server)); server.getClients().add(new CustomConnectedClient(event.getClient(), server));
} catch (Exception e) { } catch (Exception e) {
server.getProtocolBridge().getLogger().exception("Failed to add client to server", e); server.getProtocolBridge().getProtocolValues().logger.exception("Failed to add client to server", e);
event.getClient().disconnect(); event.getClient().disconnect();
} }
} }
@@ -67,7 +67,7 @@ public final class CustomServerListener extends EventListener {
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) {
server.getProtocolBridge().getLogger().exception("Failed to send web response", e); server.getProtocolBridge().getProtocolValues().logger.exception("Failed to send web response", e);
} }
} }
} }

View File

@@ -50,7 +50,7 @@ public final class AuthPacket extends OACPacket {
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.getProtocolValues().protocolVersion.name());
String caPem = "N/A"; String caPem = "N/A";
@@ -64,7 +64,7 @@ public final class AuthPacket extends OACPacket {
protocolBridge.getProtocolServer().getFolderStructure().publicCAFolder, protocolBridge.getProtocolServer().getFolderStructure().publicCAFolder,
caPrefix + ".pem")); caPrefix + ".pem"));
} catch (Exception exception) { } catch (Exception exception) {
protocolBridge.getLogger().exception("Failed to read ca-files", exception); protocolBridge.getProtocolValues().logger.exception("Failed to read ca-files", exception);
setResponseCode(INSResponseStatus.RESPONSE_AUTH_FAILED); setResponseCode(INSResponseStatus.RESPONSE_AUTH_FAILED);
} }
@@ -74,7 +74,7 @@ public final class AuthPacket extends OACPacket {
if (protocolBridge.isRunningAsServer()) { if (protocolBridge.isRunningAsServer()) {
objectOutputStream.writeBoolean(false); objectOutputStream.writeBoolean(false);
objectOutputStream.writeUTF(protocolBridge.getProtocolVersion().name()); objectOutputStream.writeUTF(protocolBridge.getProtocolValues().protocolVersion.name());
return; return;
} }
@@ -92,7 +92,7 @@ public final class AuthPacket extends OACPacket {
} }
objectOutputStream.writeUTF(clientConnectionId.toString()); objectOutputStream.writeUTF(clientConnectionId.toString());
objectOutputStream.writeUTF(protocolBridge.getProtocolVersion().name()); objectOutputStream.writeUTF(protocolBridge.getProtocolValues().protocolVersion.name());
} }
} }
@@ -197,7 +197,7 @@ public final class AuthPacket extends OACPacket {
try { try {
FileUtils.writeFile(caPemFile, caPem); FileUtils.writeFile(caPemFile, caPem);
} catch (Exception exception) { } catch (Exception exception) {
protocolBridge.getLogger().exception("Failed to create/save ca-files", exception); protocolBridge.getProtocolValues().logger.exception("Failed to create/save ca-files", exception);
setResponseCode(INSResponseStatus.RESPONSE_AUTH_FAILED); setResponseCode(INSResponseStatus.RESPONSE_AUTH_FAILED);
} }

View File

@@ -1,7 +1,11 @@
package org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.document; package org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.document;
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.TransportProtocol;
import lombok.Getter; import lombok.Getter;
import org.openautonomousconnection.protocol.ProtocolBridge;
import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.WebPacket; import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.WebPacket;
import org.openautonomousconnection.protocol.side.server.CustomConnectedClient;
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_1.beta.WebPacketHeader; import org.openautonomousconnection.protocol.versions.v1_0_1.beta.WebPacketHeader;
@@ -16,16 +20,18 @@ import java.util.UUID;
@Getter @Getter
public final class WebDocumentApplyRequestPacket extends WebPacket { public final class WebDocumentApplyRequestPacket extends WebPacket {
private String fullHtml; private String fullHtml;
private final ProtocolBridge protocolBridge;
/** /**
* Registration constructor. * Registration constructor.
*/ */
public WebDocumentApplyRequestPacket() { public WebDocumentApplyRequestPacket(ProtocolBridge protocolBridge) {
super(18, ProtocolVersion.PV_1_0_1_BETA); super(18, ProtocolVersion.PV_1_0_1_BETA);
this.protocolBridge = protocolBridge;
} }
public WebDocumentApplyRequestPacket(WebPacketHeader header, String fullHtml) { public WebDocumentApplyRequestPacket(WebPacketHeader header, String fullHtml, ProtocolBridge protocolBridge) {
this(); this(protocolBridge);
setHeader(header); setHeader(header);
this.fullHtml = (fullHtml != null) ? fullHtml : ""; this.fullHtml = (fullHtml != null) ? fullHtml : "";
} }
@@ -38,5 +44,11 @@ public final class WebDocumentApplyRequestPacket extends WebPacket {
@Override @Override
protected void readBody(DataInputStream in, UUID clientID) throws IOException { protected void readBody(DataInputStream in, UUID clientID) throws IOException {
this.fullHtml = in.readUTF(); this.fullHtml = in.readUTF();
if (protocolBridge.isRunningAsWebServer()) {
ProtocolWebServer server = (ProtocolWebServer) protocolBridge.getProtocolServer();
CustomConnectedClient client = server.getClientByID(clientID);
client.getConnection().sendPacket(server.handleDocumentApply(client, this), TransportProtocol.TCP);
}
} }
} }

View File

@@ -1,7 +1,11 @@
package org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.navigate; package org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.navigate;
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.TransportProtocol;
import lombok.Getter; import lombok.Getter;
import org.openautonomousconnection.protocol.ProtocolBridge;
import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.WebPacket; import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.WebPacket;
import org.openautonomousconnection.protocol.side.server.CustomConnectedClient;
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_1.beta.WebCacheMode; 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.WebPacketHeader;
@@ -10,6 +14,7 @@ import org.openautonomousconnection.protocol.versions.v1_0_1.beta.WebTransitionT
import java.io.DataInputStream; import java.io.DataInputStream;
import java.io.DataOutputStream; import java.io.DataOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.Objects;
import java.util.UUID; import java.util.UUID;
/** /**
@@ -22,12 +27,14 @@ public final class WebNavigateRequestPacket extends WebPacket {
private WebTransitionType transitionType; private WebTransitionType transitionType;
private long headerProfileId; private long headerProfileId;
private WebCacheMode cacheMode; private WebCacheMode cacheMode;
private final ProtocolBridge protocolBridge;
/** /**
* Registration constructor. * Registration constructor.
*/ */
public WebNavigateRequestPacket() { public WebNavigateRequestPacket(ProtocolBridge protocolBridge) {
super(1, ProtocolVersion.PV_1_0_1_BETA); super(1, ProtocolVersion.PV_1_0_1_BETA);
this.protocolBridge = Objects.requireNonNull(protocolBridge, "protocolBridge");
} }
public WebNavigateRequestPacket( public WebNavigateRequestPacket(
@@ -36,9 +43,9 @@ public final class WebNavigateRequestPacket extends WebPacket {
String referrer, String referrer,
WebTransitionType transitionType, WebTransitionType transitionType,
long headerProfileId, long headerProfileId,
WebCacheMode cacheMode WebCacheMode cacheMode, ProtocolBridge protocolBridge
) { ) {
this(); this(protocolBridge);
setHeader(header); setHeader(header);
this.url = (url != null) ? url : ""; this.url = (url != null) ? url : "";
this.referrer = referrer; this.referrer = referrer;
@@ -68,5 +75,11 @@ public final class WebNavigateRequestPacket extends WebPacket {
this.transitionType = WebTransitionType.valueOf(in.readUTF()); this.transitionType = WebTransitionType.valueOf(in.readUTF());
this.headerProfileId = in.readLong(); this.headerProfileId = in.readLong();
this.cacheMode = WebCacheMode.valueOf(in.readUTF()); this.cacheMode = WebCacheMode.valueOf(in.readUTF());
if (protocolBridge.isRunningAsWebServer()) {
ProtocolWebServer server = (ProtocolWebServer) protocolBridge.getProtocolServer();
CustomConnectedClient client = server.getClientByID(clientID);
client.getConnection().sendPacket(server.handleNavigate(client, this), TransportProtocol.TCP);
}
} }
} }

View File

@@ -1,7 +1,11 @@
package org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.resource; package org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.resource;
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.TransportProtocol;
import lombok.Getter; import lombok.Getter;
import org.openautonomousconnection.protocol.ProtocolBridge;
import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.WebPacket; import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.WebPacket;
import org.openautonomousconnection.protocol.side.server.CustomConnectedClient;
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_1.beta.WebCacheMode; 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.WebInitiatorType;
@@ -27,12 +31,14 @@ public final class WebResourceRequestPacket extends WebPacket {
private String bodyContentType; private String bodyContentType;
private WebInitiatorType initiatorType; private WebInitiatorType initiatorType;
private WebCacheMode cacheMode; private WebCacheMode cacheMode;
private final ProtocolBridge protocolBridge;
/** /**
* Registration constructor. * Registration constructor.
*/ */
public WebResourceRequestPacket() { public WebResourceRequestPacket(ProtocolBridge protocolBridge) {
super(3, ProtocolVersion.PV_1_0_1_BETA); super(3, ProtocolVersion.PV_1_0_1_BETA);
this.protocolBridge = protocolBridge;
} }
public WebResourceRequestPacket( public WebResourceRequestPacket(
@@ -43,9 +49,9 @@ public final class WebResourceRequestPacket extends WebPacket {
byte[] body, byte[] body,
String bodyContentType, String bodyContentType,
WebInitiatorType initiatorType, WebInitiatorType initiatorType,
WebCacheMode cacheMode WebCacheMode cacheMode, ProtocolBridge protocolBridge
) { ) {
this(); this(protocolBridge);
setHeader(header); setHeader(header);
this.url = (url != null) ? url : ""; this.url = (url != null) ? url : "";
this.method = (method != null) ? method : "GET"; this.method = (method != null) ? method : "GET";
@@ -90,5 +96,11 @@ public final class WebResourceRequestPacket extends WebPacket {
int len = in.readInt(); int len = in.readInt();
if (len < 0) throw new IOException("Negative body length in WebResourceRequestPacket"); if (len < 0) throw new IOException("Negative body length in WebResourceRequestPacket");
this.body = in.readNBytes(len); this.body = in.readNBytes(len);
if (protocolBridge.isRunningAsWebServer()) {
ProtocolWebServer server = (ProtocolWebServer) protocolBridge.getProtocolServer();
CustomConnectedClient client = server.getClientByID(clientID);
client.getConnection().sendPacket(server.handleResource(client, this), TransportProtocol.TCP);
}
} }
} }

View File

@@ -25,7 +25,7 @@ import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.stream
import org.openautonomousconnection.protocol.versions.ProtocolVersion; import org.openautonomousconnection.protocol.versions.ProtocolVersion;
import org.openautonomousconnection.protocol.versions.v1_0_0.beta.WebRequestMethod; 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.*;
import org.openautonomousconnection.protocol.versions.v1_0_1.beta.compat.WebCompatMapper; import org.openautonomousconnection.protocol.versions.v1_0_1.beta.WebCompatMapper;
import java.util.Collections; import java.util.Collections;
import java.util.Map; import java.util.Map;
@@ -140,7 +140,7 @@ public abstract class ProtocolWebClient extends ProtocolClient {
referrer, referrer,
transition, transition,
headerProfileId, headerProfileId,
cacheMode cacheMode, getProtocolBridge()
); );
getClientServerConnection().sendPacket(pkt, TransportProtocol.TCP); getClientServerConnection().sendPacket(pkt, TransportProtocol.TCP);
@@ -195,7 +195,7 @@ public abstract class ProtocolWebClient extends ProtocolClient {
body, body,
bodyContentType, bodyContentType,
initiator, initiator,
cacheMode cacheMode, getProtocolBridge()
); );
getClientServerConnection().sendPacket(pkt, TransportProtocol.TCP); getClientServerConnection().sendPacket(pkt, TransportProtocol.TCP);
@@ -221,7 +221,7 @@ public abstract class ProtocolWebClient extends ProtocolClient {
base.getTimestampMs() base.getTimestampMs()
); );
WebDocumentApplyRequestPacket pkt = new WebDocumentApplyRequestPacket(header, fullHtml); WebDocumentApplyRequestPacket pkt = new WebDocumentApplyRequestPacket(header, fullHtml, getProtocolBridge());
getClientServerConnection().sendPacket(pkt, TransportProtocol.TCP); getClientServerConnection().sendPacket(pkt, TransportProtocol.TCP);
onWebRequestSent(pkt); onWebRequestSent(pkt);
return pkt.getHeader().getRequestId(); return pkt.getHeader().getRequestId();

View File

@@ -65,7 +65,7 @@ public abstract class ProtocolINSServer extends ProtocolCustomServer {
java.security.MessageDigest md = java.security.MessageDigest.getInstance("SHA-256"); java.security.MessageDigest md = java.security.MessageDigest.getInstance("SHA-256");
String fp = java.util.HexFormat.of().formatHex(md.digest(caBytes)); String fp = java.util.HexFormat.of().formatHex(md.digest(caBytes));
getProtocolBridge().getLogger().info("CA Fingerprint: " + fp); getProtocolBridge().getProtocolValues().logger.info("CA Fingerprint: " + fp);
} }
/** /**

View File

@@ -1,5 +1,9 @@
package org.openautonomousconnection.protocol.side.web; package org.openautonomousconnection.protocol.side.web;
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.utils.TransportProtocol;
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;
@@ -17,7 +21,7 @@ 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_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.WebPacketFlags;
import org.openautonomousconnection.protocol.versions.v1_0_1.beta.WebPacketHeader; import org.openautonomousconnection.protocol.versions.v1_0_1.beta.WebPacketHeader;
import org.openautonomousconnection.protocol.versions.v1_0_1.beta.compat.WebCompatMapper; import org.openautonomousconnection.protocol.versions.v1_0_1.beta.WebCompatMapper;
import java.io.File; import java.io.File;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
@@ -59,6 +63,38 @@ public abstract class ProtocolWebServer extends ProtocolWebServer_1_0_0_B {
super(authFile, rulesFile, sessionExpire, uploadSize); super(authFile, rulesFile, sessionExpire, uploadSize);
} }
@Listener(priority = EventPriority.HIGHEST)
public final void onPacketRead(C_PacketReadEvent event) {
Objects.requireNonNull(event, "event");
Object packet = event.getPacket();
if (packet == null) return;
CustomConnectedClient client = getClientByID(event.getClient().getUniqueID());
if (client == null) return;
try {
if (packet instanceof WebNavigateRequestPacket nav) {
WebNavigateAckPacket ack = handleNavigate(client, nav);
client.getConnection().sendPacket(ack, TransportProtocol.TCP);
return;
}
if (packet instanceof WebResourceRequestPacket req) {
WebResourceResponsePacket resp = handleResource(client, req);
client.getConnection().sendPacket(resp, TransportProtocol.TCP);
return;
}
if (packet instanceof WebDocumentApplyRequestPacket apply) {
WebDocumentApplyResponsePacket resp = handleDocumentApply(client, apply);
client.getConnection().sendPacket(resp, TransportProtocol.TCP);
}
} catch (Exception e) {
getProtocolBridge().getProtocolValues().logger.exception("Failed to handle packet", e);
}
}
/** /**
* Server-side dispatcher entry point for a navigation request (packet id 01). * Server-side dispatcher entry point for a navigation request (packet id 01).
* *
@@ -184,7 +220,7 @@ public abstract class ProtocolWebServer extends ProtocolWebServer_1_0_0_B {
long tabId = LEGACY_TAB_ID; long tabId = LEGACY_TAB_ID;
long pageId = LEGACY_PAGE_ID; long pageId = LEGACY_PAGE_ID;
WebResourceRequestPacket mapped = WebCompatMapper.toResourceRequest(requestId, tabId, pageId, request); WebResourceRequestPacket mapped = WebCompatMapper.toResourceRequest(requestId, tabId, pageId, request, getProtocolBridge());
WebResourceResponsePacket resp = onResourceRequest(client, mapped); WebResourceResponsePacket resp = onResourceRequest(client, mapped);
if (resp == null) { if (resp == null) {

View File

@@ -417,21 +417,12 @@ public final class OacWebRequestBroker {
connectionLatch = new CountDownLatch(1); connectionLatch = new CountDownLatch(1);
INSQueryPacket query = new INSQueryPacket(
tln,
name,
sub,
INSRecordType.A,
client.getClientINSConnection().getUniqueID()
);
try { try {
client.getClientINSConnection().sendPacket(query, TransportProtocol.TCP); client.sendINSQuery(tln, name, sub, INSRecordType.A);
awaitConnectionIfPending();
} catch (Exception e) { } catch (Exception e) {
throw new IllegalStateException("Failed to send INSQueryPacket for " + infoName, e); throw new IllegalStateException("Failed to send INSQueryPacket for " + infoName, e);
} }
awaitConnectionIfPending();
} }
private void awaitConnectionIfPending() { private void awaitConnectionIfPending() {

View File

@@ -0,0 +1,293 @@
package org.openautonomousconnection.protocol.urlhandler.v1_0_1.beta;
import dev.unlegitdqrk.unlegitlibrary.event.EventListener;
import dev.unlegitdqrk.unlegitlibrary.event.EventPriority;
import dev.unlegitdqrk.unlegitlibrary.event.Listener;
import dev.unlegitdqrk.unlegitlibrary.network.system.client.events.packets.C_PacketReadEvent;
import org.openautonomousconnection.protocol.ProtocolBridge;
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.INSResponsePacket;
import org.openautonomousconnection.protocol.side.client.ProtocolClient;
import org.openautonomousconnection.protocol.side.client.events.ConnectedToProtocolINSServerEvent;
import org.openautonomousconnection.protocol.side.client.events.ConnectedToProtocolServerEvent;
import org.openautonomousconnection.protocol.urlhandler.v1_0_1.beta.web.OacWebRequestBroker;
import org.openautonomousconnection.protocol.versions.v1_0_0.beta.INSRecord;
import org.openautonomousconnection.protocol.versions.v1_0_0.beta.INSRecordType;
import org.openautonomousconnection.protocol.versions.v1_0_0.beta.INSResponseStatus;
import java.util.List;
import java.util.Objects;
/**
* Coordinates INS resolution for WEB requests and connects to the resolved server endpoint.
*
* <p>No {@code sendIns()} exists. This coordinator sends INS queries directly:</p>
* <ul>
* <li>{@link #requestResolve(String)} is called on every WEB request.</li>
* <li>Requests for the currently connected InfoName reuse the active server connection.</li>
* <li>If INS is connected, unresolved hosts immediately trigger {@code sendINSQuery(tln,name,sub,A)}.</li>
* <li>If INS is not connected yet, it stores the pending host and sends on {@link ConnectedToProtocolINSServerEvent}.</li>
* <li>On {@link INSResponsePacket} it connects the server connection.</li>
* <li>On {@link ConnectedToProtocolServerEvent} it opens the broker gate.</li>
* </ul>
*/
public final class InsResolutionCoordinator_v1_0_1_B extends EventListener {
private static final InsResolutionCoordinator_v1_0_1_B INSTANCE = new InsResolutionCoordinator_v1_0_1_B();
private final Object lock = new Object();
private volatile ProtocolBridge bridge;
private volatile ProtocolClient insClient; // from ConnectedToProtocolINSServerEvent
private volatile boolean insConnected;
private volatile String activeInfoName;
private volatile String pendingInfoName;
private InsResolutionCoordinator_v1_0_1_B() {
}
/**
* @return global singleton instance
*/
public static InsResolutionCoordinator_v1_0_1_B get() {
return INSTANCE;
}
/**
* Attaches runtime dependencies.
*
* @param protocolBridge protocol bridge
*/
public synchronized void attach(ProtocolBridge protocolBridge) {
Objects.requireNonNull(protocolBridge, "protocolBridge");
if (this.bridge == null) {
this.bridge = protocolBridge;
return;
}
if (this.bridge == protocolBridge) return;
throw new IllegalStateException("InsResolutionCoordinator runtime already initialized with different instances");
}
/**
* Called by the WEB broker for EVERY outgoing request.
*
* <p>If the requested InfoName is already connected, the current server connection is reused.
* Otherwise an INS query is triggered for the URL host. If INS is not connected yet, the host is stored
* and will be sent once INS connects.</p>
*
* @param infoName host from {@code web://<infoName>/...}
*/
public void requestResolve(String infoName) {
String in = (infoName == null) ? "" : infoName.trim();
if (in.isEmpty()) {
throw new IllegalArgumentException("InfoName is empty");
}
if (canReuseCurrentServerConnection(in)) {
OacWebRequestBroker.get().notifyServerConnected();
return;
}
synchronized (lock) {
pendingInfoName = in;
}
// Unresolved or changed hosts reset the server gate and trigger INS -> connect -> request.
OacWebRequestBroker.get().beginServerConnectAttempt();
// If INS already connected, send immediately
trySendPendingInsNow();
}
/**
* INS is now connected -> we can send pending INS immediately.
*
* @param event ins connected event
*/
@Listener(priority = EventPriority.HIGHEST)
public void onConnectedToIns(ConnectedToProtocolINSServerEvent event) {
Objects.requireNonNull(event, "event");
this.insClient = Objects.requireNonNull(event.getClient(), "event.getClient()");
this.insConnected = true;
// Send pending (if any) now that INS is connected
trySendPendingInsNow();
}
/**
* Receives INS resolution response and starts the WEB server connect attempt.
*
* @param event packet read event
*/
@Listener(priority = EventPriority.HIGHEST)
public void onInsResponse(C_PacketReadEvent event) {
Objects.requireNonNull(event, "event");
if (!(event.getPacket() instanceof INSResponsePacket packet)) return;
final String infoName;
final String hostname;
final int port;
try {
synchronized (lock) {
infoName = pendingInfoName;
}
if (infoName == null || infoName.isBlank()) {
throw new IllegalStateException("INS response received without pending info name");
}
INSResponseStatus status = packet.getStatus();
List<INSRecord> records = packet.getRecords();
if (status != INSResponseStatus.OK) {
throw new IllegalStateException("INS resolution failed: " + status);
}
if (records == null || records.isEmpty() || records.getFirst() == null || records.getFirst().value == null) {
throw new IllegalStateException("INS resolution returned no usable records");
}
String host = records.getFirst().value.trim();
if (host.isEmpty()) {
throw new IllegalStateException("INS record value is empty");
}
if (!host.contains(":")) {
hostname = host;
int recordPort = records.getFirst().port;
port = (recordPort == 0) ? 1028 : recordPort;
} else {
String[] split = host.split(":", 2);
String h = split[0].trim();
String p1 = split[1].trim();
if (h.isEmpty() || p1.isEmpty()) {
throw new IllegalStateException("Invalid INS host:port value: " + host);
}
try {
port = Integer.parseInt(p1);
} catch (NumberFormatException e) {
throw new IllegalStateException("Invalid port in INS record: " + host, e);
}
hostname = h;
}
} catch (Exception e) {
OacWebRequestBroker.get().notifyServerConnectionFailed(e);
return;
}
Thread t = new Thread(() -> connectServer(infoName, hostname, port), "oac-web-server-connect");
t.setDaemon(true);
t.start();
}
/**
* Server connected -> open broker gate.
*
* @param event server connected event
*/
@Listener(priority = EventPriority.HIGHEST)
public void onConnectedToServer(ConnectedToProtocolServerEvent event) {
Objects.requireNonNull(event, "event");
OacWebRequestBroker.get().notifyServerConnected();
}
private boolean canReuseCurrentServerConnection(String infoName) {
ProtocolBridge b = this.bridge;
if (b == null) return false;
ProtocolClient client = b.getProtocolClient();
if (client == null || client.getClientServerConnection() == null || !client.getClientServerConnection().isConnected()) {
return false;
}
return infoName.equalsIgnoreCase(activeInfoName);
}
private void trySendPendingInsNow() {
ProtocolClient c = this.insClient;
if (c == null) return;
final String infoName;
synchronized (lock) {
infoName = pendingInfoName;
}
if (infoName == null || infoName.isBlank()) return;
InsParts parts = InsParts.parse(infoName);
try {
// IMPORTANT: direct INS query, no impl.sendIns()
c.sendINSQuery(parts.tln(), parts.name(), parts.sub(), INSRecordType.A);
} catch (Exception e) {
OacWebRequestBroker.get().notifyServerConnectionFailed(e);
}
}
private void connectServer(String infoName, String hostname, int port) {
ProtocolBridge b = this.bridge;
if (b == null) return;
if (canReuseCurrentServerConnection(infoName)) {
OacWebRequestBroker.get().notifyServerConnected();
return;
}
try {
ProtocolClient client = b.getProtocolClient();
if (client.getClientServerConnection() != null && client.getClientServerConnection().isConnected()) {
client.getClientServerConnection().disconnect();
}
client.buildServerConnection(
null,
client.getProtocolBridge().getProtocolValues().ssl
);
client.getClientServerConnection().connect(hostname, port);
this.activeInfoName = infoName;
// Broker gate is opened by ConnectedToProtocolServerEvent.
} catch (Exception e) {
OacWebRequestBroker.get().notifyServerConnectionFailed(e);
}
}
/**
* Parses name.tln or sub.name.tln.
*/
public record InsParts(String tln, String name, String sub) {
/**
* Parses an InfoName into INS parts.
*
* @param infoName infoName (name.tln or sub.name.tln)
* @return parts
*/
public static InsParts parse(String infoName) {
String in = Objects.requireNonNull(infoName, "infoName").trim();
if (in.isEmpty()) throw new IllegalArgumentException("infoName is empty");
String[] parts = in.split("\\.");
if (parts.length < 2 || parts.length > 3) {
throw new IllegalArgumentException(
"Invalid INS address format: " + infoName + " (expected name.tln or sub.name.tln)"
);
}
String tln = parts[parts.length - 1];
String name = parts[parts.length - 2];
String sub = (parts.length == 3) ? parts[0] : null;
return new InsParts(tln, name, sub);
}
}
}

View File

@@ -2,6 +2,7 @@ package org.openautonomousconnection.protocol.urlhandler.v1_0_1.beta;
import org.openautonomousconnection.protocol.ProtocolBridge; import org.openautonomousconnection.protocol.ProtocolBridge;
import org.openautonomousconnection.protocol.urlhandler.v1_0_1.beta.web.OacWebProtocolModule_v1_0_1_B; import org.openautonomousconnection.protocol.urlhandler.v1_0_1.beta.web.OacWebProtocolModule_v1_0_1_B;
import org.openautonomousconnection.protocol.urlhandler.v1_0_1.beta.web.OacWebRequestBroker;
import org.openautonomousconnection.protocol.urlhandler.v1_0_1.beta.web.WebFlagInspector; import org.openautonomousconnection.protocol.urlhandler.v1_0_1.beta.web.WebFlagInspector;
import org.openautonomousconnection.protocol.urlhandler.v1_0_1.beta.web.WebRequestContextProvider; import org.openautonomousconnection.protocol.urlhandler.v1_0_1.beta.web.WebRequestContextProvider;
@@ -11,6 +12,13 @@ import java.util.concurrent.atomic.AtomicBoolean;
/** /**
* Installs OAC URL protocol modules for v1.0.1-BETA. * Installs OAC URL protocol modules for v1.0.1-BETA.
* *
* <p>Runtime behavior required by this installer:</p>
* <ul>
* <li>Every WEB request triggers an INS resolve attempt (via {@link InsResolutionCoordinator_v1_0_1_B}).</li>
* <li>Once an INS response arrives, the client connects to the resolved server endpoint.</li>
* <li>Only after the server connection is established, WEB packets may be sent (broker gate).</li>
* </ul>
*
* <p>Call once during startup (before creating {@link java.net.URL} instances).</p> * <p>Call once during startup (before creating {@link java.net.URL} instances).</p>
*/ */
public final class OacUrlHandlerInstaller_v1_0_1_B { public final class OacUrlHandlerInstaller_v1_0_1_B {
@@ -24,9 +32,9 @@ public final class OacUrlHandlerInstaller_v1_0_1_B {
* Installs the URL handler package path and registers the WEB module. * Installs the URL handler package path and registers the WEB module.
* *
* @param protocolBridge protocol bridge * @param protocolBridge protocol bridge
* @param impl callback implementation * @param impl callback implementation
* @param ctxProvider provides tab/page/frame correlation for each request * @param ctxProvider provides tab/page/frame correlation for each request
* @param flagInspector interprets {@code WebPacketHeader.flags} (e.g. stream bit) * @param flagInspector interprets {@code WebPacketHeader.flags} (e.g. stream bit)
*/ */
public static void installOnce( public static void installOnce(
ProtocolBridge protocolBridge, ProtocolBridge protocolBridge,
@@ -43,8 +51,18 @@ public final class OacUrlHandlerInstaller_v1_0_1_B {
ProtocolHandlerPackages.installPackage("org.openautonomousconnection.protocol.urlhandler.v1_0_1.beta"); ProtocolHandlerPackages.installPackage("org.openautonomousconnection.protocol.urlhandler.v1_0_1.beta");
OacWebProtocolModule_v1_0_1_B web = new OacWebProtocolModule_v1_0_1_B(ctxProvider, flagInspector); // WEB module (URLConnection + web packet listener -> broker)
OacWebProtocolModule_v1_0_1_B web = new OacWebProtocolModule_v1_0_1_B(ctxProvider, flagInspector, protocolBridge);
OacUrlProtocolRegistry.register(web); OacUrlProtocolRegistry.register(web);
web.install(protocolBridge, impl); web.install(protocolBridge, impl);
// INS coordination (request -> send INS -> connect server -> open broker gate)
InsResolutionCoordinator_v1_0_1_B coordinator = InsResolutionCoordinator_v1_0_1_B.get();
coordinator.attach(protocolBridge);
protocolBridge.getProtocolValues().eventManager.registerListener(coordinator);
// Broker needs to wait for "server connected" gate. Coordinator will open/fail this gate.
OacWebRequestBroker.get().attachCoordinator(coordinator);
} }
} }

View File

@@ -1,5 +1,7 @@
package org.openautonomousconnection.protocol.urlhandler.v1_0_1.beta.web; package org.openautonomousconnection.protocol.urlhandler.v1_0_1.beta.web;
import org.openautonomousconnection.protocol.ProtocolBridge;
import java.io.*; import java.io.*;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.ProtocolException; import java.net.ProtocolException;
@@ -18,6 +20,7 @@ public final class OacWebHttpURLConnection extends HttpURLConnection {
private final OacWebRequestBroker broker; private final OacWebRequestBroker broker;
private final OacSessionJar sessionJar; private final OacSessionJar sessionJar;
private final WebRequestContextProvider ctxProvider; private final WebRequestContextProvider ctxProvider;
private final ProtocolBridge bridge;
private final Map<String, String> requestHeaders = new LinkedHashMap<>(); private final Map<String, String> requestHeaders = new LinkedHashMap<>();
private final ByteArrayOutputStream requestBody = new ByteArrayOutputStream(1024); private final ByteArrayOutputStream requestBody = new ByteArrayOutputStream(1024);
@@ -25,11 +28,12 @@ public final class OacWebHttpURLConnection extends HttpURLConnection {
private boolean requestSent; private boolean requestSent;
private OacWebResponse response; private OacWebResponse response;
public OacWebHttpURLConnection(URL url, OacWebRequestBroker broker, OacSessionJar sessionJar, WebRequestContextProvider ctxProvider) { public OacWebHttpURLConnection(URL url, OacWebRequestBroker broker, OacSessionJar sessionJar, WebRequestContextProvider ctxProvider, ProtocolBridge protocolBridge) {
super(url); super(url);
this.broker = Objects.requireNonNull(broker, "broker"); this.broker = Objects.requireNonNull(broker, "broker");
this.sessionJar = Objects.requireNonNull(sessionJar, "sessionJar"); this.sessionJar = Objects.requireNonNull(sessionJar, "sessionJar");
this.ctxProvider = Objects.requireNonNull(ctxProvider, "ctxProvider"); this.ctxProvider = Objects.requireNonNull(ctxProvider, "ctxProvider");
this.bridge = Objects.requireNonNull(protocolBridge, "bridge");
this.method = "GET"; this.method = "GET";
setInstanceFollowRedirects(true); setInstanceFollowRedirects(true);
} }
@@ -131,7 +135,7 @@ public final class OacWebHttpURLConnection extends HttpURLConnection {
carryBody, carryBody,
ctx.tabId(), ctx.tabId(),
ctx.pageId(), ctx.pageId(),
ctx.frameId() ctx.frameId(), bridge
); );
String newSession = broker.extractSession(resp.headers()); String newSession = broker.extractSession(resp.headers());

View File

@@ -4,6 +4,7 @@ import dev.unlegitdqrk.unlegitlibrary.event.EventListener;
import dev.unlegitdqrk.unlegitlibrary.event.EventPriority; 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.packets.C_PacketReadEvent; import dev.unlegitdqrk.unlegitlibrary.network.system.client.events.packets.C_PacketReadEvent;
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet;
import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.resource.WebResourceResponsePacket; 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.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.WebStreamEndPacket_v1_0_1_B;
@@ -34,8 +35,7 @@ public final class OacWebPacketListener extends EventListener {
@Listener(priority = EventPriority.HIGHEST) @Listener(priority = EventPriority.HIGHEST)
public void onPacketRead(C_PacketReadEvent event) { public void onPacketRead(C_PacketReadEvent event) {
Object p = event.getPacket(); Packet p = event.getPacket();
if (p instanceof WebResourceResponsePacket resp) { if (p instanceof WebResourceResponsePacket resp) {
broker.onResourceResponse(resp); broker.onResourceResponse(resp);
return; return;

View File

@@ -17,10 +17,12 @@ public final class OacWebProtocolModule_v1_0_1_B implements OacUrlProtocolModule
private final OacSessionJar sessionJar = new OacSessionJar(); private final OacSessionJar sessionJar = new OacSessionJar();
private final WebRequestContextProvider ctxProvider; private final WebRequestContextProvider ctxProvider;
private final WebFlagInspector flagInspector; private final WebFlagInspector flagInspector;
private final ProtocolBridge bridge;
public OacWebProtocolModule_v1_0_1_B(WebRequestContextProvider ctxProvider, WebFlagInspector flagInspector) { public OacWebProtocolModule_v1_0_1_B(WebRequestContextProvider ctxProvider, WebFlagInspector flagInspector, ProtocolBridge protocolBridge) {
this.ctxProvider = Objects.requireNonNull(ctxProvider, "ctxProvider"); this.ctxProvider = Objects.requireNonNull(ctxProvider, "ctxProvider");
this.flagInspector = Objects.requireNonNull(flagInspector, "flagInspector"); this.flagInspector = Objects.requireNonNull(flagInspector, "flagInspector");
this.bridge = Objects.requireNonNull(protocolBridge, "protocolBridge");
} }
@Override @Override
@@ -34,7 +36,7 @@ public final class OacWebProtocolModule_v1_0_1_B implements OacUrlProtocolModule
if (!"web".equalsIgnoreCase(url.getProtocol())) { if (!"web".equalsIgnoreCase(url.getProtocol())) {
throw new IOException("Unsupported scheme for this module: " + url.getProtocol()); throw new IOException("Unsupported scheme for this module: " + url.getProtocol());
} }
return new OacWebHttpURLConnection(url, OacWebRequestBroker.get(), sessionJar, ctxProvider); return new OacWebHttpURLConnection(url, OacWebRequestBroker.get(), sessionJar, ctxProvider, bridge);
} }
@Override @Override

View File

@@ -1,12 +1,14 @@
package org.openautonomousconnection.protocol.urlhandler.v1_0_1.beta.web; package org.openautonomousconnection.protocol.urlhandler.v1_0_1.beta.web;
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.TransportProtocol; import dev.unlegitdqrk.unlegitlibrary.network.system.utils.TransportProtocol;
import org.openautonomousconnection.protocol.ProtocolBridge;
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.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.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.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.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.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.urlhandler.v1_0_1.beta.InsResolutionCoordinator_v1_0_1_B;
import org.openautonomousconnection.protocol.versions.v1_0_1.beta.WebPacketFlags; 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.WebPacketHeader;
@@ -14,31 +16,34 @@ import java.io.*;
import java.net.URL; import java.net.URL;
import java.nio.file.*; import java.nio.file.*;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
/** /**
* Multi-flight broker for WEB v1.0.1-BETA with best-effort streaming and temp-file assembly. * Multi-flight broker for WEB v1.0.1-BETA with best-effort streaming and temp-file assembly.
* *
* <p>Range support: request headers are passed through unchanged (e.g. "Range").</p> * <p><b>Gate behavior:</b> Before any WEB packet is sent, the broker triggers INS resolve (every request),
* * then blocks until server connection is established (opened by {@link #notifyServerConnected()}).</p>
* <p>Streaming is best-effort: missing sequence numbers are ignored.</p>
* <p>Chunks are spooled to temp chunk files and merged into a final temp file on stream end.</p>
*/ */
public final class OacWebRequestBroker { public final class OacWebRequestBroker {
private static final OacWebRequestBroker INSTANCE = new OacWebRequestBroker(); private static final OacWebRequestBroker INSTANCE = new OacWebRequestBroker();
private static final long RESPONSE_TIMEOUT_SECONDS = 30; private static final long RESPONSE_TIMEOUT_SECONDS = 30;
private static final long CONNECT_TIMEOUT_SECONDS = 30;
private final ConcurrentHashMap<Long, ResponseState> inFlight = new ConcurrentHashMap<>(); private final ConcurrentHashMap<Long, ResponseState> inFlight = new ConcurrentHashMap<>();
private final AtomicLong requestCounter = new AtomicLong(1); private final AtomicLong requestCounter = new AtomicLong(1);
private final Object connectLock = new Object();
private volatile CompletableFuture<Void> serverReady = new CompletableFuture<>();
private volatile Throwable serverFailure;
private volatile ProtocolClient client; private volatile ProtocolClient client;
private volatile WebFlagInspector flagInspector; private volatile WebFlagInspector flagInspector;
private volatile InsResolutionCoordinator_v1_0_1_B coordinator;
private OacWebRequestBroker() { private OacWebRequestBroker() {
} }
@@ -52,8 +57,6 @@ public final class OacWebRequestBroker {
/** /**
* Attaches runtime dependencies required for dispatching requests and interpreting flags. * Attaches runtime dependencies required for dispatching requests and interpreting flags.
* *
* <p>Safe to call multiple times with the same instances. Different instances are rejected.</p>
*
* @param client protocol client used to send packets * @param client protocol client used to send packets
* @param flagInspector inspector for header flags (STREAM bit) * @param flagInspector inspector for header flags (STREAM bit)
*/ */
@@ -67,24 +70,100 @@ public final class OacWebRequestBroker {
return; return;
} }
if (this.client == client && this.flagInspector == flagInspector) { if (this.client == client && this.flagInspector == flagInspector) return;
return;
}
throw new IllegalStateException("OacWebRequestBroker runtime already initialized with different instances"); throw new IllegalStateException("OacWebRequestBroker runtime already initialized with different instances");
} }
/**
* Attaches the INS coordinator used for per-request INS resolution.
*
* @param coordinator coordinator
*/
public synchronized void attachCoordinator(InsResolutionCoordinator_v1_0_1_B coordinator) {
Objects.requireNonNull(coordinator, "coordinator");
if (this.coordinator == null) {
this.coordinator = coordinator;
return;
}
if (this.coordinator == coordinator) return;
throw new IllegalStateException("Coordinator already attached with different instance");
}
/**
* Resets the server connection gate to "not ready".
*/
public void beginServerConnectAttempt() {
synchronized (connectLock) {
this.serverFailure = null;
this.serverReady = new CompletableFuture<>();
}
}
/**
* Opens the server connection gate.
*/
public void notifyServerConnected() {
synchronized (connectLock) {
this.serverFailure = null;
CompletableFuture<Void> f = this.serverReady;
if (!f.isDone()) f.complete(null);
}
}
/**
* Fails the server connection gate and unblocks waiters.
*
* @param t failure cause
*/
public void notifyServerConnectionFailed(Throwable t) {
Objects.requireNonNull(t, "t");
synchronized (connectLock) {
this.serverFailure = t;
CompletableFuture<Void> f = this.serverReady;
if (!f.isDone()) f.completeExceptionally(t);
}
}
private void awaitServerConnection() throws IOException {
ProtocolClient c = this.client;
if (c == null) {
throw new IllegalStateException("ProtocolClient not attached. Call OacWebRequestBroker.attachRuntime(...) during install.");
}
if (c.getClientServerConnection() != null && c.getClientServerConnection().isConnected()) {
return;
}
CompletableFuture<Void> f = this.serverReady;
try {
f.get(CONNECT_TIMEOUT_SECONDS, TimeUnit.SECONDS);
} catch (TimeoutException e) {
throw new IOException("Timeout waiting for server connection after " + CONNECT_TIMEOUT_SECONDS + "s", e);
} catch (ExecutionException e) {
Throwable cause = (e.getCause() == null) ? e : e.getCause();
throw new IOException("Server connection failed: " + cause.getMessage(), cause);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new IOException("Interrupted while waiting for server connection", e);
}
if (c.getClientServerConnection() == null || !c.getClientServerConnection().isConnected()) {
Throwable fail = this.serverFailure;
if (fail != null) throw new IOException("Server connection failed: " + fail.getMessage(), fail);
throw new IOException("Server gate opened but connection is not connected");
}
}
/** /**
* Sends a resource request and blocks until completion. * Sends a resource request and blocks until completion.
* *
* @param url requested URL * <p><b>Required flow:</b></p>
* @param method HTTP-like method (GET/POST/...) * <ol>
* @param headers request headers (passed through unchanged; includes Range) * <li>Trigger INS query for URL host (EVERY request).</li>
* @param body request body (nullable) * <li>Wait for INS response -> connect -> server connected gate.</li>
* @param tabId tab id * <li>Send WEB request packet.</li>
* @param pageId page id * </ol>
* @param frameId frame id
* @return resolved response
*/ */
public OacWebResponse fetch( public OacWebResponse fetch(
URL url, URL url,
@@ -93,10 +172,26 @@ public final class OacWebRequestBroker {
byte[] body, byte[] body,
long tabId, long tabId,
long pageId, long pageId,
long frameId long frameId, ProtocolBridge bridge
) throws IOException { ) throws IOException {
Objects.requireNonNull(url, "url"); Objects.requireNonNull(url, "url");
String host = url.getHost();
if (host == null || host.isBlank()) {
throw new IOException("Missing InfoName in URL host: " + url);
}
InsResolutionCoordinator_v1_0_1_B coord = this.coordinator;
if (coord == null) {
throw new IllegalStateException("INS coordinator not attached. Ensure installer was called.");
}
// Requirement: trigger INS on EVERY request.
coord.requestResolve(host);
// Block until server is connected
awaitServerConnection();
ProtocolClient c = this.client; ProtocolClient c = this.client;
if (c == null) { if (c == null) {
throw new IllegalStateException("ProtocolClient not attached. Call OacWebRequestBroker.attachRuntime(...) during install."); throw new IllegalStateException("ProtocolClient not attached. Call OacWebRequestBroker.attachRuntime(...) during install.");
@@ -105,7 +200,6 @@ public final class OacWebRequestBroker {
String m = (method == null || method.isBlank()) ? "GET" : method.trim().toUpperCase(Locale.ROOT); String m = (method == null || method.isBlank()) ? "GET" : method.trim().toUpperCase(Locale.ROOT);
long requestId = requestCounter.getAndIncrement(); long requestId = requestCounter.getAndIncrement();
int flags = WebPacketFlags.RESOURCE; int flags = WebPacketFlags.RESOURCE;
WebPacketHeader header = new WebPacketHeader( WebPacketHeader header = new WebPacketHeader(
@@ -131,7 +225,7 @@ public final class OacWebRequestBroker {
safeBody, safeBody,
safeHeaders.get("content-type"), safeHeaders.get("content-type"),
null, null,
null null, bridge
); );
try { try {
@@ -149,23 +243,10 @@ public final class OacWebRequestBroker {
return awaitAndBuildResponse(st); return awaitAndBuildResponse(st);
} }
/**
* Extracts the session token from response headers (case-insensitive).
*
* @param headers headers
* @return session token or null
*/
public String extractSession(Map<String, String> headers) { public String extractSession(Map<String, String> headers) {
return headerValue(headers, "session"); return headerValue(headers, "session");
} }
/**
* Handles a WebResourceResponsePacket.
*
* <p>If STREAM flag is set in header, body is expected to be streamed via start/chunk/end packets.</p>
*
* @param p packet
*/
public void onResourceResponse(WebResourceResponsePacket p) { public void onResourceResponse(WebResourceResponsePacket p) {
if (p == null || p.getHeader() == null) return; if (p == null || p.getHeader() == null) return;
@@ -195,16 +276,9 @@ public final class OacWebRequestBroker {
} }
st.streamExpected = true; st.streamExpected = true;
// Some servers may already include metadata here; do not complete until stream end.
} }
} }
/**
* Handles stream start.
*
* @param p start packet
*/
public void onStreamStart(WebStreamStartPacket_v1_0_1_B p) { public void onStreamStart(WebStreamStartPacket_v1_0_1_B p) {
if (p == null || p.getHeader() == null) return; if (p == null || p.getHeader() == null) return;
@@ -232,11 +306,6 @@ public final class OacWebRequestBroker {
} }
} }
/**
* Handles stream chunk (best-effort; stores chunk by seq).
*
* @param p chunk packet
*/
public void onStreamChunk(WebStreamChunkPacket_v1_0_1_B p) { public void onStreamChunk(WebStreamChunkPacket_v1_0_1_B p) {
if (p == null || p.getHeader() == null) return; if (p == null || p.getHeader() == null) return;
@@ -273,14 +342,6 @@ public final class OacWebRequestBroker {
} }
} }
/**
* Handles stream end and assembles best-effort output.
*
* <p>On success, assembles final temp file and returns full content bytes (optional; may be large).</p>
*
* @param p end packet
* @return full content bytes (best-effort) or null on failure
*/
public byte[] onStreamEndAndAssemble(WebStreamEndPacket_v1_0_1_B p) { public byte[] onStreamEndAndAssemble(WebStreamEndPacket_v1_0_1_B p) {
if (p == null || p.getHeader() == null) return null; if (p == null || p.getHeader() == null) return null;
@@ -300,7 +361,6 @@ public final class OacWebRequestBroker {
} }
if (st.spooler == null) { if (st.spooler == null) {
// No chunks received; treat as empty success.
st.finalFile = null; st.finalFile = null;
st.completed = true; st.completed = true;
st.done.countDown(); st.done.countDown();
@@ -324,14 +384,11 @@ public final class OacWebRequestBroker {
st.completed = true; st.completed = true;
st.done.countDown(); st.done.countDown();
// Optional: deliver full bytes to callback (requested).
// WARNING: This can be large. You explicitly requested it, so we do it.
if (st.finalFile == null) return new byte[0]; if (st.finalFile == null) return new byte[0];
try (InputStream in = Files.newInputStream(st.finalFile)) { try (InputStream in = Files.newInputStream(st.finalFile)) {
return in.readAllBytes(); return in.readAllBytes();
} catch (IOException e) { } catch (IOException e) {
// Assembly is still fine; just no full byte[].
return null; return null;
} }
} }
@@ -349,7 +406,6 @@ public final class OacWebRequestBroker {
throw new IOException("Interrupted while waiting for web response (requestId=" + st.requestId + ")", e); throw new IOException("Interrupted while waiting for web response (requestId=" + st.requestId + ")", e);
} }
// Remove from map; response is completed now.
inFlight.remove(st.requestId); inFlight.remove(st.requestId);
synchronized (st.lock) { synchronized (st.lock) {
@@ -359,7 +415,6 @@ public final class OacWebRequestBroker {
throw new IOException(msg); throw new IOException(msg);
} }
// Non-stream response
if (!st.streamExpected && st.finalFile == null) { if (!st.streamExpected && st.finalFile == null) {
byte[] b = (st.memoryBody == null) ? new byte[0] : st.memoryBody; byte[] b = (st.memoryBody == null) ? new byte[0] : st.memoryBody;
return new OacWebResponse( return new OacWebResponse(
@@ -371,9 +426,7 @@ public final class OacWebRequestBroker {
); );
} }
// Stream response -> return InputStream backed by final temp file (delete-on-close)
if (st.finalFile == null) { if (st.finalFile == null) {
// Valid empty stream
return new OacWebResponse( return new OacWebResponse(
st.statusCode, st.statusCode,
safeContentType(st.contentType), safeContentType(st.contentType),
@@ -384,7 +437,6 @@ public final class OacWebRequestBroker {
} }
InputStream fileIn = Files.newInputStream(st.finalFile, StandardOpenOption.READ); InputStream fileIn = Files.newInputStream(st.finalFile, StandardOpenOption.READ);
// Delete final file + spool directory when WebView/URLConnection closes the stream.
return new OacWebResponse( return new OacWebResponse(
st.statusCode, st.statusCode,
safeContentType(st.contentType), safeContentType(st.contentType),
@@ -419,10 +471,7 @@ public final class OacWebRequestBroker {
} }
if (st.spoolDir != null) { if (st.spoolDir != null) {
try { try {
// Let DeleteOnCloseInputStream handle recursive deletion in success path,
// but ensure cleanup on failure/timeout.
if (Files.exists(st.spoolDir)) { if (Files.exists(st.spoolDir)) {
// best-effort delete
try (DirectoryStream<Path> ds = Files.newDirectoryStream(st.spoolDir)) { try (DirectoryStream<Path> ds = Files.newDirectoryStream(st.spoolDir)) {
for (Path p : ds) { for (Path p : ds) {
try { try {
@@ -477,9 +526,6 @@ public final class OacWebRequestBroker {
return null; return null;
} }
/**
* Per-request state (multi-flight, correlated by requestId).
*/
private static final class ResponseState { private static final class ResponseState {
private final long requestId; private final long requestId;
private final Object lock = new Object(); private final Object lock = new Object();
@@ -509,9 +555,6 @@ public final class OacWebRequestBroker {
} }
} }
/**
* Spools stream chunks to temp files and assembles best-effort output on end.
*/
private static final class TempChunkSpooler implements Closeable { private static final class TempChunkSpooler implements Closeable {
private final Path dir; private final Path dir;
@@ -526,7 +569,6 @@ public final class OacWebRequestBroker {
} }
void writeChunk(int seq, byte[] data) throws IOException { void writeChunk(int seq, byte[] data) throws IOException {
// One file per seq -> no offset assumptions needed.
Path p = dir.resolve(seq + ".chunk"); Path p = dir.resolve(seq + ".chunk");
Files.write(p, data, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE); Files.write(p, data, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE);
} }
@@ -537,9 +579,7 @@ public final class OacWebRequestBroker {
try (OutputStream os = Files.newOutputStream(out, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE)) { try (OutputStream os = Files.newOutputStream(out, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE)) {
for (int seq = 0; seq <= maxSeqSeen; seq++) { for (int seq = 0; seq <= maxSeqSeen; seq++) {
Path p = dir.resolve(seq + ".chunk"); Path p = dir.resolve(seq + ".chunk");
if (!Files.exists(p)) { if (!Files.exists(p)) continue;
continue; // best-effort: skip gaps
}
try (InputStream in = Files.newInputStream(p, StandardOpenOption.READ)) { try (InputStream in = Files.newInputStream(p, StandardOpenOption.READ)) {
in.transferTo(os); in.transferTo(os);
} }
@@ -556,7 +596,6 @@ public final class OacWebRequestBroker {
@Override @Override
public void close() throws IOException { public void close() throws IOException {
// Cleanup remaining chunk files (best-effort).
if (!Files.exists(dir)) return; if (!Files.exists(dir)) return;
try (DirectoryStream<Path> ds = Files.newDirectoryStream(dir)) { try (DirectoryStream<Path> ds = Files.newDirectoryStream(dir)) {

View File

@@ -63,14 +63,14 @@ public final class INSRecordTools {
private static List<INSRecord> followCNAME(ProtocolINSServer server, String tln, String name, String sub, INSRecordType targetType, int depth, Set<String> visited) { private static List<INSRecord> followCNAME(ProtocolINSServer server, String tln, String name, String sub, INSRecordType targetType, int depth, Set<String> visited) {
if (depth > MAX_CNAME_DEPTH) { if (depth > MAX_CNAME_DEPTH) {
server.getProtocolBridge().getLogger().warn("Max CNAME depth exceeded for " + fqdn(tln, name, sub)); server.getProtocolBridge().getProtocolValues().logger.warn("Max CNAME depth exceeded for " + fqdn(tln, name, sub));
return Collections.emptyList(); return Collections.emptyList();
} }
String key = fqdn(tln, name, sub); String key = fqdn(tln, name, sub);
if (!visited.add(key)) { if (!visited.add(key)) {
// Loop detected // Loop detected
server.getProtocolBridge().getLogger().warn("CNAME loop detected for " + key); server.getProtocolBridge().getProtocolValues().logger.warn("CNAME loop detected for " + key);
return Collections.emptyList(); return Collections.emptyList();
} }

View File

@@ -0,0 +1,46 @@
package org.openautonomousconnection.protocol.versions.v1_0_1.beta;
import java.util.Objects;
/**
* Parsed INS address components.
*
* <p>Supported formats:</p>
* <ul>
* <li>{@code name.tln}</li>
* <li>{@code sub.name.tln}</li>
* </ul>
*/
public record InsParts(String tln, String name, String sub) {
/**
* Parses an InfoName into INS query parts.
*
* @param infoName InfoName host string
* @return parsed parts
* @throws IllegalArgumentException if format is invalid
*/
public static InsParts parse(String infoName) {
String in = Objects.requireNonNull(infoName, "infoName").trim();
if (in.isEmpty()) {
throw new IllegalArgumentException("InfoName is empty");
}
String[] parts = in.split("\\.");
if (parts.length < 2 || parts.length > 3) {
throw new IllegalArgumentException(
"Invalid INS address format: " + infoName + " (expected name.tln or sub.name.tln)"
);
}
String tln = parts[parts.length - 1].trim();
String name = parts[parts.length - 2].trim();
String sub = (parts.length == 3) ? parts[0].trim() : null;
if (tln.isEmpty() || name.isEmpty() || (sub != null && sub.isEmpty())) {
throw new IllegalArgumentException("Invalid INS address parts in: " + infoName);
}
return new InsParts(tln, name, sub);
}
}

View File

@@ -1,5 +1,6 @@
package org.openautonomousconnection.protocol.versions.v1_0_1.beta.compat; package org.openautonomousconnection.protocol.versions.v1_0_1.beta;
import org.openautonomousconnection.protocol.ProtocolBridge;
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_v1_0_0_B; import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.stream.WebStreamChunkPacket_v1_0_0_B;
@@ -11,10 +12,6 @@ import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.stream
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.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.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_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.Collections;
import java.util.Map; import java.util.Map;
@@ -70,7 +67,7 @@ public final class WebCompatMapper {
long requestId, long requestId,
long tabId, long tabId,
long pageId, long pageId,
WebRequestPacket requestPacket WebRequestPacket requestPacket, ProtocolBridge bridge
) { ) {
Objects.requireNonNull(requestPacket, "requestPacket"); Objects.requireNonNull(requestPacket, "requestPacket");
@@ -96,7 +93,7 @@ public final class WebCompatMapper {
body, body,
null, null,
WebInitiatorType.OTHER, WebInitiatorType.OTHER,
WebCacheMode.DEFAULT WebCacheMode.DEFAULT, bridge
); );
} }