Call once during startup (before creating {@link java.net.URL} instances).
*/
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);
}
}
\ No newline at end of file
diff --git a/src/main/java/org/openautonomousconnection/protocol/urlhandler/v1_0_1/beta/web/OacWebHttpURLConnection.java b/src/main/java/org/openautonomousconnection/protocol/urlhandler/v1_0_1/beta/web/OacWebHttpURLConnection.java
index 411e39c..659e4bb 100644
--- a/src/main/java/org/openautonomousconnection/protocol/urlhandler/v1_0_1/beta/web/OacWebHttpURLConnection.java
+++ b/src/main/java/org/openautonomousconnection/protocol/urlhandler/v1_0_1/beta/web/OacWebHttpURLConnection.java
@@ -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 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());
diff --git a/src/main/java/org/openautonomousconnection/protocol/urlhandler/v1_0_1/beta/web/OacWebPacketListener.java b/src/main/java/org/openautonomousconnection/protocol/urlhandler/v1_0_1/beta/web/OacWebPacketListener.java
index 1ed2154..7bafd1f 100644
--- a/src/main/java/org/openautonomousconnection/protocol/urlhandler/v1_0_1/beta/web/OacWebPacketListener.java
+++ b/src/main/java/org/openautonomousconnection/protocol/urlhandler/v1_0_1/beta/web/OacWebPacketListener.java
@@ -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;
diff --git a/src/main/java/org/openautonomousconnection/protocol/urlhandler/v1_0_1/beta/web/OacWebProtocolModule_v1_0_1_B.java b/src/main/java/org/openautonomousconnection/protocol/urlhandler/v1_0_1/beta/web/OacWebProtocolModule_v1_0_1_B.java
index 348c233..d74e921 100644
--- a/src/main/java/org/openautonomousconnection/protocol/urlhandler/v1_0_1/beta/web/OacWebProtocolModule_v1_0_1_B.java
+++ b/src/main/java/org/openautonomousconnection/protocol/urlhandler/v1_0_1/beta/web/OacWebProtocolModule_v1_0_1_B.java
@@ -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
diff --git a/src/main/java/org/openautonomousconnection/protocol/urlhandler/v1_0_1/beta/web/OacWebRequestBroker.java b/src/main/java/org/openautonomousconnection/protocol/urlhandler/v1_0_1/beta/web/OacWebRequestBroker.java
index 8088a23..289b76d 100644
--- a/src/main/java/org/openautonomousconnection/protocol/urlhandler/v1_0_1/beta/web/OacWebRequestBroker.java
+++ b/src/main/java/org/openautonomousconnection/protocol/urlhandler/v1_0_1/beta/web/OacWebRequestBroker.java
@@ -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.
*
- * Range support: request headers are passed through unchanged (e.g. "Range").
- *
- * Streaming is best-effort: missing sequence numbers are ignored.
- * Chunks are spooled to temp chunk files and merged into a final temp file on stream end.
+ * Gate behavior: Before any WEB packet is sent, the broker triggers INS resolve (every request),
+ * then blocks until server connection is established (opened by {@link #notifyServerConnected()}).
*/
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 inFlight = new ConcurrentHashMap<>();
private final AtomicLong requestCounter = new AtomicLong(1);
+ private final Object connectLock = new Object();
+ private volatile CompletableFuture 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.
*
- * Safe to call multiple times with the same instances. Different instances are rejected.
- *
* @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 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 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 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
+ * Required flow:
+ *
+ * - Trigger INS query for URL host (EVERY request).
+ * - Wait for INS response -> connect -> server connected gate.
+ * - Send WEB request packet.
+ *
*/
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 headers) {
return headerValue(headers, "session");
}
- /**
- * Handles a WebResourceResponsePacket.
- *
- * If STREAM flag is set in header, body is expected to be streamed via start/chunk/end packets.
- *
- * @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.
- *
- * On success, assembles final temp file and returns full content bytes (optional; may be large).
- *
- * @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 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 ds = Files.newDirectoryStream(dir)) {
diff --git a/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/beta/INSRecordTools.java b/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/beta/INSRecordTools.java
index 4ea5dc5..ba3eb8b 100644
--- a/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/beta/INSRecordTools.java
+++ b/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/beta/INSRecordTools.java
@@ -63,14 +63,14 @@ public final class INSRecordTools {
private static List followCNAME(ProtocolINSServer server, String tln, String name, String sub, INSRecordType targetType, int depth, Set 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();
}
diff --git a/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_1/beta/InsParts.java b/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_1/beta/InsParts.java
new file mode 100644
index 0000000..4be05f2
--- /dev/null
+++ b/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_1/beta/InsParts.java
@@ -0,0 +1,46 @@
+package org.openautonomousconnection.protocol.versions.v1_0_1.beta;
+
+import java.util.Objects;
+
+/**
+ * Parsed INS address components.
+ *
+ * Supported formats:
+ *
+ * - {@code name.tln}
+ * - {@code sub.name.tln}
+ *
+ */
+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);
+ }
+}
diff --git a/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_1/beta/compat/WebCompatMapper.java b/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_1/beta/WebCompatMapper.java
similarity index 96%
rename from src/main/java/org/openautonomousconnection/protocol/versions/v1_0_1/beta/compat/WebCompatMapper.java
rename to src/main/java/org/openautonomousconnection/protocol/versions/v1_0_1/beta/WebCompatMapper.java
index 2cacef6..ecf2cac 100644
--- a/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_1/beta/compat/WebCompatMapper.java
+++ b/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_1/beta/WebCompatMapper.java
@@ -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
);
}