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

@@ -40,6 +40,7 @@ import org.openautonomousconnection.protocol.versions.v1_0_0.beta.ProtocolWebSer
import java.io.File;
import java.io.IOException;
import java.util.ResourceBundle;
import java.util.function.Supplier;
/**
@@ -53,18 +54,6 @@ public final class ProtocolBridge {
@Getter
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
*/
@@ -78,27 +67,17 @@ public final class ProtocolBridge {
@Getter
private ProtocolCustomServer protocolServer;
@Getter
private AddonLoader addonLoader;
/**
* Initialize the ProtocolBridge instance for the client side
*
* @param protocolServer The ProtocolCustomServer 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
*/
public ProtocolBridge(ProtocolCustomServer protocolServer, ProtocolValues protocolValues, ProtocolVersion protocolVersion,
Logger logger, AddonLoader addonLoader) throws Exception {
public ProtocolBridge(ProtocolCustomServer protocolServer, ProtocolValues protocolValues) throws Exception {
// Assign the parameters to the class fields
this.protocolServer = protocolServer;
this.protocolValues = protocolValues;
this.protocolVersion = protocolVersion;
this.logger = logger;
this.addonLoader = addonLoader;
if (protocolServer instanceof ProtocolINSServer)
protocolServer.attachBridge(this, null, false, ClientAuthMode.NONE);
@@ -118,20 +97,14 @@ public final class ProtocolBridge {
*
* @param protocolClient The ProtocolClient 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
*/
@ProtocolInfo(protocolSide = ProtocolVersion.ProtocolSide.CLIENT)
public ProtocolBridge(ProtocolClient protocolClient, ProtocolValues protocolValues, ProtocolVersion protocolVersion,
Logger logger, AddonLoader addonLoader, LibClientImpl_v1_0_1_B libClientImpl) throws Exception {
public ProtocolBridge(ProtocolClient protocolClient, LibClientImpl_v1_0_1_B libClientImpl,
ProtocolValues protocolValues) throws Exception {
// Assign the parameters to the class fields
this.protocolClient = protocolClient;
this.protocolValues = protocolValues;
this.protocolVersion = protocolVersion;
this.logger = logger;
this.addonLoader = addonLoader;
protocolClient.attachBridge(this);
initializeProtocolVersion();
@@ -145,11 +118,11 @@ public final class ProtocolBridge {
}
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);
}
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);
}
}
@@ -211,12 +184,12 @@ public final class ProtocolBridge {
registerPacket(WebStreamEndPacket_v1_0_0_B::new);
// 1.0.1-BETA Packets
registerPacket(WebDocumentApplyRequestPacket::new);
registerPacket(() -> new WebDocumentApplyRequestPacket(this));
registerPacket(WebDocumentApplyResponsePacket::new);
registerPacket(WebDocumentSnapshotEventPacket::new);
registerPacket(WebNavigateRequestPacket::new);
registerPacket(() -> new WebNavigateRequestPacket(this));
registerPacket(WebNavigateAckPacket::new);
registerPacket(WebResourceRequestPacket::new);
registerPacket(() -> new WebResourceRequestPacket(this));
registerPacket(WebResourceResponsePacket::new);
registerPacket(WebStreamStartPacket_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
// If not, log an error and exit the application
if (!validateProtocolSide()) {
this.logger.error("Invalid protocol version '" + protocolVersion.toString() + "'!");
protocolValues.logger.error("Invalid protocol version '" + protocolValues.protocolVersion.toString() + "'!");
System.exit(1);
}
}
@@ -267,14 +240,14 @@ public final class ProtocolBridge {
*/
public boolean isClassicSupported() {
boolean yes = false;
for (ProtocolVersion compatibleVersion : protocolVersion.getCompatibleVersions()) {
for (ProtocolVersion compatibleVersion : protocolValues.protocolVersion.getCompatibleVersions()) {
// Check if the compatible version is classic
yes = compatibleVersion.getProtocolType() == ProtocolVersion.ProtocolType.CLASSIC;
if (yes) break;
}
// 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) {
boolean yes = false;
for (ProtocolVersion compatibleVersion : protocolVersion.getCompatibleVersions()) {
for (ProtocolVersion compatibleVersion : protocolValues.protocolVersion.getCompatibleVersions()) {
// Check if the compatible version supports the target protocol
yes = compatibleVersion.getSupportedProtocols().contains(protocol);
if (yes) break;
}
// 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) {
// 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() {
return
(isRunningAsClient() && protocolVersion.getProtocolSide() == ProtocolVersion.ProtocolSide.CLIENT) ||
(isRunningAsClient() && protocolVersion.getProtocolSide() == ProtocolVersion.ProtocolSide.CLIENT_WEB) ||
(isRunningAsClient() && protocolVersion.getProtocolSide() == ProtocolVersion.ProtocolSide.CLIENT_INS) ||
(isRunningAsClient() && protocolVersion.getProtocolSide() == ProtocolVersion.ProtocolSide.ALL) ||
(isRunningAsClient() && protocolValues.protocolVersion.getProtocolSide() == ProtocolVersion.ProtocolSide.CLIENT) ||
(isRunningAsClient() && protocolValues.protocolVersion.getProtocolSide() == ProtocolVersion.ProtocolSide.CLIENT_WEB) ||
(isRunningAsClient() && protocolValues.protocolVersion.getProtocolSide() == ProtocolVersion.ProtocolSide.CLIENT_INS) ||
(isRunningAsClient() && protocolValues.protocolVersion.getProtocolSide() == ProtocolVersion.ProtocolSide.ALL) ||
(isRunningAsWebServer() && protocolVersion.getProtocolSide() == ProtocolVersion.ProtocolSide.WEB) ||
(isRunningAsWebServer() && protocolVersion.getProtocolSide() == ProtocolVersion.ProtocolSide.CLIENT_WEB) ||
(isRunningAsWebServer() && protocolVersion.getProtocolSide() == ProtocolVersion.ProtocolSide.WEB_INS) ||
(isRunningAsWebServer() && protocolVersion.getProtocolSide() == ProtocolVersion.ProtocolSide.ALL) ||
(isRunningAsWebServer() && protocolValues.protocolVersion.getProtocolSide() == ProtocolVersion.ProtocolSide.WEB) ||
(isRunningAsWebServer() && protocolValues.protocolVersion.getProtocolSide() == ProtocolVersion.ProtocolSide.CLIENT_WEB) ||
(isRunningAsWebServer() && protocolValues.protocolVersion.getProtocolSide() == ProtocolVersion.ProtocolSide.WEB_INS) ||
(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() && protocolVersion.getProtocolSide() == ProtocolVersion.ProtocolSide.WEB_INS) ||
(isRunningAsINSServer() && protocolVersion.getProtocolSide() == ProtocolVersion.ProtocolSide.CLIENT_INS) ||
(isRunningAsINSServer() && protocolVersion.getProtocolSide() == ProtocolVersion.ProtocolSide.ALL);
(isRunningAsINSServer() && protocolValues.protocolVersion.getProtocolSide() == ProtocolVersion.ProtocolSide.INS) ||
(isRunningAsINSServer() && protocolValues.protocolVersion.getProtocolSide() == ProtocolVersion.ProtocolSide.WEB_INS) ||
(isRunningAsINSServer() && protocolValues.protocolVersion.getProtocolSide() == ProtocolVersion.ProtocolSide.CLIENT_INS) ||
(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
*/
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;
}

View File

@@ -1,9 +1,12 @@
package org.openautonomousconnection.protocol;
import dev.unlegitdqrk.unlegitlibrary.addon.AddonLoader;
import dev.unlegitdqrk.unlegitlibrary.event.EventManager;
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.PacketHandler;
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.ClientAuthMode;
import dev.unlegitdqrk.unlegitlibrary.utils.DefaultMethodsOverrider;
import dev.unlegitdqrk.unlegitlibrary.utils.Logger;
import org.openautonomousconnection.protocol.versions.ProtocolVersion;
/**
* Settings for the protocol connection.
@@ -19,8 +22,14 @@ public final class ProtocolValues extends DefaultMethodsOverrider {
*/
public EventManager eventManager;
public AddonLoader addonLoader;
public Logger logger;
public String keyPass = null;
public ProtocolVersion protocolVersion;
public boolean ssl = true;
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.
*/
@Deprecated(forRemoval = false, since = "1.0.1-BETA.0.5")
@Retention(RetentionPolicy.RUNTIME)
public @interface ProtocolInfo {

View File

@@ -43,7 +43,7 @@ public final class ClientListener extends EventListener {
try {
event.getClient().sendPacket(new AuthPacket(client.getProtocolBridge()), TransportProtocol.TCP);
} 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();
}
}

View File

@@ -52,7 +52,7 @@ public final class CustomServerListener extends EventListener {
try {
server.getClients().add(new CustomConnectedClient(event.getClient(), server));
} 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();
}
}
@@ -67,7 +67,7 @@ public final class CustomServerListener extends EventListener {
onWebRequest(server.getClientByID(event.getClient().getUniqueID()), (WebRequestPacket) event.getPacket()),
TransportProtocol.TCP);
} 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 {
if (protocolBridge.isRunningAsINSServer()) {
objectOutputStream.writeBoolean(true);
objectOutputStream.writeUTF(protocolBridge.getProtocolVersion().name());
objectOutputStream.writeUTF(protocolBridge.getProtocolValues().protocolVersion.name());
String caPem = "N/A";
@@ -64,7 +64,7 @@ public final class AuthPacket extends OACPacket {
protocolBridge.getProtocolServer().getFolderStructure().publicCAFolder,
caPrefix + ".pem"));
} 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);
}
@@ -74,7 +74,7 @@ public final class AuthPacket extends OACPacket {
if (protocolBridge.isRunningAsServer()) {
objectOutputStream.writeBoolean(false);
objectOutputStream.writeUTF(protocolBridge.getProtocolVersion().name());
objectOutputStream.writeUTF(protocolBridge.getProtocolValues().protocolVersion.name());
return;
}
@@ -92,7 +92,7 @@ public final class AuthPacket extends OACPacket {
}
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 {
FileUtils.writeFile(caPemFile, caPem);
} 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);
}

View File

@@ -1,7 +1,11 @@
package org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.document;
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.TransportProtocol;
import lombok.Getter;
import org.openautonomousconnection.protocol.ProtocolBridge;
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.v1_0_1.beta.WebPacketHeader;
@@ -16,16 +20,18 @@ import java.util.UUID;
@Getter
public final class WebDocumentApplyRequestPacket extends WebPacket {
private String fullHtml;
private final ProtocolBridge protocolBridge;
/**
* Registration constructor.
*/
public WebDocumentApplyRequestPacket() {
public WebDocumentApplyRequestPacket(ProtocolBridge protocolBridge) {
super(18, ProtocolVersion.PV_1_0_1_BETA);
this.protocolBridge = protocolBridge;
}
public WebDocumentApplyRequestPacket(WebPacketHeader header, String fullHtml) {
this();
public WebDocumentApplyRequestPacket(WebPacketHeader header, String fullHtml, ProtocolBridge protocolBridge) {
this(protocolBridge);
setHeader(header);
this.fullHtml = (fullHtml != null) ? fullHtml : "";
}
@@ -38,5 +44,11 @@ public final class WebDocumentApplyRequestPacket extends WebPacket {
@Override
protected void readBody(DataInputStream in, UUID clientID) throws IOException {
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;
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.TransportProtocol;
import lombok.Getter;
import org.openautonomousconnection.protocol.ProtocolBridge;
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.v1_0_1.beta.WebCacheMode;
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.DataOutputStream;
import java.io.IOException;
import java.util.Objects;
import java.util.UUID;
/**
@@ -22,12 +27,14 @@ public final class WebNavigateRequestPacket extends WebPacket {
private WebTransitionType transitionType;
private long headerProfileId;
private WebCacheMode cacheMode;
private final ProtocolBridge protocolBridge;
/**
* Registration constructor.
*/
public WebNavigateRequestPacket() {
public WebNavigateRequestPacket(ProtocolBridge protocolBridge) {
super(1, ProtocolVersion.PV_1_0_1_BETA);
this.protocolBridge = Objects.requireNonNull(protocolBridge, "protocolBridge");
}
public WebNavigateRequestPacket(
@@ -36,9 +43,9 @@ public final class WebNavigateRequestPacket extends WebPacket {
String referrer,
WebTransitionType transitionType,
long headerProfileId,
WebCacheMode cacheMode
WebCacheMode cacheMode, ProtocolBridge protocolBridge
) {
this();
this(protocolBridge);
setHeader(header);
this.url = (url != null) ? url : "";
this.referrer = referrer;
@@ -68,5 +75,11 @@ public final class WebNavigateRequestPacket extends WebPacket {
this.transitionType = WebTransitionType.valueOf(in.readUTF());
this.headerProfileId = in.readLong();
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;
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.TransportProtocol;
import lombok.Getter;
import org.openautonomousconnection.protocol.ProtocolBridge;
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.v1_0_1.beta.WebCacheMode;
import org.openautonomousconnection.protocol.versions.v1_0_1.beta.WebInitiatorType;
@@ -27,12 +31,14 @@ public final class WebResourceRequestPacket extends WebPacket {
private String bodyContentType;
private WebInitiatorType initiatorType;
private WebCacheMode cacheMode;
private final ProtocolBridge protocolBridge;
/**
* Registration constructor.
*/
public WebResourceRequestPacket() {
public WebResourceRequestPacket(ProtocolBridge protocolBridge) {
super(3, ProtocolVersion.PV_1_0_1_BETA);
this.protocolBridge = protocolBridge;
}
public WebResourceRequestPacket(
@@ -43,9 +49,9 @@ public final class WebResourceRequestPacket extends WebPacket {
byte[] body,
String bodyContentType,
WebInitiatorType initiatorType,
WebCacheMode cacheMode
WebCacheMode cacheMode, ProtocolBridge protocolBridge
) {
this();
this(protocolBridge);
setHeader(header);
this.url = (url != null) ? url : "";
this.method = (method != null) ? method : "GET";
@@ -90,5 +96,11 @@ public final class WebResourceRequestPacket extends WebPacket {
int len = in.readInt();
if (len < 0) throw new IOException("Negative body length in WebResourceRequestPacket");
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.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 org.openautonomousconnection.protocol.versions.v1_0_1.beta.WebCompatMapper;
import java.util.Collections;
import java.util.Map;
@@ -140,7 +140,7 @@ public abstract class ProtocolWebClient extends ProtocolClient {
referrer,
transition,
headerProfileId,
cacheMode
cacheMode, getProtocolBridge()
);
getClientServerConnection().sendPacket(pkt, TransportProtocol.TCP);
@@ -195,7 +195,7 @@ public abstract class ProtocolWebClient extends ProtocolClient {
body,
bodyContentType,
initiator,
cacheMode
cacheMode, getProtocolBridge()
);
getClientServerConnection().sendPacket(pkt, TransportProtocol.TCP);
@@ -221,7 +221,7 @@ public abstract class ProtocolWebClient extends ProtocolClient {
base.getTimestampMs()
);
WebDocumentApplyRequestPacket pkt = new WebDocumentApplyRequestPacket(header, fullHtml);
WebDocumentApplyRequestPacket pkt = new WebDocumentApplyRequestPacket(header, fullHtml, getProtocolBridge());
getClientServerConnection().sendPacket(pkt, TransportProtocol.TCP);
onWebRequestSent(pkt);
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");
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;
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.packets.v1_0_0.beta.web.WebRequestPacket;
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_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 org.openautonomousconnection.protocol.versions.v1_0_1.beta.WebCompatMapper;
import java.io.File;
import java.nio.charset.StandardCharsets;
@@ -59,6 +63,38 @@ public abstract class ProtocolWebServer extends ProtocolWebServer_1_0_0_B {
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).
*
@@ -184,7 +220,7 @@ public abstract class ProtocolWebServer extends ProtocolWebServer_1_0_0_B {
long tabId = LEGACY_TAB_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);
if (resp == null) {

View File

@@ -417,21 +417,12 @@ public final class OacWebRequestBroker {
connectionLatch = new CountDownLatch(1);
INSQueryPacket query = new INSQueryPacket(
tln,
name,
sub,
INSRecordType.A,
client.getClientINSConnection().getUniqueID()
);
try {
client.getClientINSConnection().sendPacket(query, TransportProtocol.TCP);
client.sendINSQuery(tln, name, sub, INSRecordType.A);
awaitConnectionIfPending();
} catch (Exception e) {
throw new IllegalStateException("Failed to send INSQueryPacket for " + infoName, e);
}
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.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.WebRequestContextProvider;
@@ -11,6 +12,13 @@ import java.util.concurrent.atomic.AtomicBoolean;
/**
* 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>
*/
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.
*
* @param protocolBridge protocol bridge
* @param impl callback implementation
* @param ctxProvider provides tab/page/frame correlation for each request
* @param flagInspector interprets {@code WebPacketHeader.flags} (e.g. stream bit)
* @param impl callback implementation
* @param ctxProvider provides tab/page/frame correlation for each request
* @param flagInspector interprets {@code WebPacketHeader.flags} (e.g. stream bit)
*/
public static void installOnce(
ProtocolBridge protocolBridge,
@@ -43,8 +51,18 @@ public final class OacUrlHandlerInstaller_v1_0_1_B {
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);
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;
import org.openautonomousconnection.protocol.ProtocolBridge;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.ProtocolException;
@@ -18,6 +20,7 @@ public final class OacWebHttpURLConnection extends HttpURLConnection {
private final OacWebRequestBroker broker;
private final OacSessionJar sessionJar;
private final WebRequestContextProvider ctxProvider;
private final ProtocolBridge bridge;
private final Map<String, String> requestHeaders = new LinkedHashMap<>();
private final ByteArrayOutputStream requestBody = new ByteArrayOutputStream(1024);
@@ -25,11 +28,12 @@ public final class OacWebHttpURLConnection extends HttpURLConnection {
private boolean requestSent;
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);
this.broker = Objects.requireNonNull(broker, "broker");
this.sessionJar = Objects.requireNonNull(sessionJar, "sessionJar");
this.ctxProvider = Objects.requireNonNull(ctxProvider, "ctxProvider");
this.bridge = Objects.requireNonNull(protocolBridge, "bridge");
this.method = "GET";
setInstanceFollowRedirects(true);
}
@@ -131,7 +135,7 @@ public final class OacWebHttpURLConnection extends HttpURLConnection {
carryBody,
ctx.tabId(),
ctx.pageId(),
ctx.frameId()
ctx.frameId(), bridge
);
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.Listener;
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.stream.WebStreamChunkPacket_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)
public void onPacketRead(C_PacketReadEvent event) {
Object p = event.getPacket();
Packet p = event.getPacket();
if (p instanceof WebResourceResponsePacket resp) {
broker.onResourceResponse(resp);
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 WebRequestContextProvider ctxProvider;
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.flagInspector = Objects.requireNonNull(flagInspector, "flagInspector");
this.bridge = Objects.requireNonNull(protocolBridge, "protocolBridge");
}
@Override
@@ -34,7 +36,7 @@ public final class OacWebProtocolModule_v1_0_1_B implements OacUrlProtocolModule
if (!"web".equalsIgnoreCase(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

View File

@@ -1,12 +1,14 @@
package org.openautonomousconnection.protocol.urlhandler.v1_0_1.beta.web;
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.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.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.WebPacketHeader;
@@ -14,31 +16,34 @@ import java.io.*;
import java.net.URL;
import java.nio.file.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
/**
* 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>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>
* <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>
*/
public final class OacWebRequestBroker {
private static final OacWebRequestBroker INSTANCE = new OacWebRequestBroker();
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 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 WebFlagInspector flagInspector;
private volatile InsResolutionCoordinator_v1_0_1_B coordinator;
private OacWebRequestBroker() {
}
@@ -52,8 +57,6 @@ public final class OacWebRequestBroker {
/**
* 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 flagInspector inspector for header flags (STREAM bit)
*/
@@ -67,24 +70,100 @@ public final class OacWebRequestBroker {
return;
}
if (this.client == client && this.flagInspector == flagInspector) {
return;
}
if (this.client == client && this.flagInspector == flagInspector) return;
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.
*
* @param url requested URL
* @param method HTTP-like method (GET/POST/...)
* @param headers request headers (passed through unchanged; includes Range)
* @param body request body (nullable)
* @param tabId tab id
* @param pageId page id
* @param frameId frame id
* @return resolved response
* <p><b>Required flow:</b></p>
* <ol>
* <li>Trigger INS query for URL host (EVERY request).</li>
* <li>Wait for INS response -> connect -> server connected gate.</li>
* <li>Send WEB request packet.</li>
* </ol>
*/
public OacWebResponse fetch(
URL url,
@@ -93,10 +172,26 @@ public final class OacWebRequestBroker {
byte[] body,
long tabId,
long pageId,
long frameId
long frameId, ProtocolBridge bridge
) throws IOException {
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;
if (c == null) {
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);
long requestId = requestCounter.getAndIncrement();
int flags = WebPacketFlags.RESOURCE;
WebPacketHeader header = new WebPacketHeader(
@@ -131,7 +225,7 @@ public final class OacWebRequestBroker {
safeBody,
safeHeaders.get("content-type"),
null,
null
null, bridge
);
try {
@@ -149,23 +243,10 @@ public final class OacWebRequestBroker {
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) {
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) {
if (p == null || p.getHeader() == null) return;
@@ -195,16 +276,9 @@ public final class OacWebRequestBroker {
}
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) {
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) {
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) {
if (p == null || p.getHeader() == null) return null;
@@ -300,7 +361,6 @@ public final class OacWebRequestBroker {
}
if (st.spooler == null) {
// No chunks received; treat as empty success.
st.finalFile = null;
st.completed = true;
st.done.countDown();
@@ -324,14 +384,11 @@ public final class OacWebRequestBroker {
st.completed = true;
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];
try (InputStream in = Files.newInputStream(st.finalFile)) {
return in.readAllBytes();
} catch (IOException e) {
// Assembly is still fine; just no full byte[].
return null;
}
}
@@ -349,7 +406,6 @@ public final class OacWebRequestBroker {
throw new IOException("Interrupted while waiting for web response (requestId=" + st.requestId + ")", e);
}
// Remove from map; response is completed now.
inFlight.remove(st.requestId);
synchronized (st.lock) {
@@ -359,7 +415,6 @@ public final class OacWebRequestBroker {
throw new IOException(msg);
}
// Non-stream response
if (!st.streamExpected && st.finalFile == null) {
byte[] b = (st.memoryBody == null) ? new byte[0] : st.memoryBody;
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) {
// Valid empty stream
return new OacWebResponse(
st.statusCode,
safeContentType(st.contentType),
@@ -384,7 +437,6 @@ public final class OacWebRequestBroker {
}
InputStream fileIn = Files.newInputStream(st.finalFile, StandardOpenOption.READ);
// Delete final file + spool directory when WebView/URLConnection closes the stream.
return new OacWebResponse(
st.statusCode,
safeContentType(st.contentType),
@@ -419,10 +471,7 @@ public final class OacWebRequestBroker {
}
if (st.spoolDir != null) {
try {
// Let DeleteOnCloseInputStream handle recursive deletion in success path,
// but ensure cleanup on failure/timeout.
if (Files.exists(st.spoolDir)) {
// best-effort delete
try (DirectoryStream<Path> ds = Files.newDirectoryStream(st.spoolDir)) {
for (Path p : ds) {
try {
@@ -477,9 +526,6 @@ public final class OacWebRequestBroker {
return null;
}
/**
* Per-request state (multi-flight, correlated by requestId).
*/
private static final class ResponseState {
private final long requestId;
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 final Path dir;
@@ -526,7 +569,6 @@ public final class OacWebRequestBroker {
}
void writeChunk(int seq, byte[] data) throws IOException {
// One file per seq -> no offset assumptions needed.
Path p = dir.resolve(seq + ".chunk");
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)) {
for (int seq = 0; seq <= maxSeqSeen; seq++) {
Path p = dir.resolve(seq + ".chunk");
if (!Files.exists(p)) {
continue; // best-effort: skip gaps
}
if (!Files.exists(p)) continue;
try (InputStream in = Files.newInputStream(p, StandardOpenOption.READ)) {
in.transferTo(os);
}
@@ -556,7 +596,6 @@ public final class OacWebRequestBroker {
@Override
public void close() throws IOException {
// Cleanup remaining chunk files (best-effort).
if (!Files.exists(dir)) return;
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) {
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();
}
String key = fqdn(tln, name, sub);
if (!visited.add(key)) {
// Loop detected
server.getProtocolBridge().getLogger().warn("CNAME loop detected for " + key);
server.getProtocolBridge().getProtocolValues().logger.warn("CNAME loop detected for " + key);
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.WebResponsePacket;
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.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;
@@ -70,7 +67,7 @@ public final class WebCompatMapper {
long requestId,
long tabId,
long pageId,
WebRequestPacket requestPacket
WebRequestPacket requestPacket, ProtocolBridge bridge
) {
Objects.requireNonNull(requestPacket, "requestPacket");
@@ -96,7 +93,7 @@ public final class WebCompatMapper {
body,
null,
WebInitiatorType.OTHER,
WebCacheMode.DEFAULT
WebCacheMode.DEFAULT, bridge
);
}