package org.openautonomousconnection.webserver.utils; import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.WebResponsePacket; import org.openautonomousconnection.webserver.api.WebPageContext; import javax.net.ssl.HttpsURLConnection; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; /** * Simple HTTPS -> OAC proxy helper. * *

Limitations: *

*/ public final class HttpsProxy { private static final int CONNECT_TIMEOUT_MS = 10_000; private static final int READ_TIMEOUT_MS = 25_000; private static final int MAX_REDIRECTS = 8; private HttpsProxy() { } /** * Fetches an HTTPS URL and returns it as a WebResponsePacket. * * @param ctx the current web page context (for optional user-agent forwarding) * @param url the target HTTPS URL * @return proxied response */ public static WebResponsePacket proxyGet(WebPageContext ctx, String url) { try { return proxyGetInternal(ctx, url, 0); } catch (Exception e) { return new WebResponsePacket( 502, "text/plain; charset=utf-8", Map.of(), ("Bad Gateway: " + e.getClass().getName() + ": " + e.getMessage()).getBytes(StandardCharsets.UTF_8) ); } } private static WebResponsePacket proxyGetInternal(WebPageContext ctx, String url, int depth) throws Exception { if (depth > MAX_REDIRECTS) { return new WebResponsePacket( 508, "text/plain; charset=utf-8", Map.of(), "Too many redirects".getBytes(StandardCharsets.UTF_8) ); } URL target = new URL(url); HttpsURLConnection con = (HttpsURLConnection) target.openConnection(); con.setInstanceFollowRedirects(false); con.setRequestMethod("GET"); con.setConnectTimeout(CONNECT_TIMEOUT_MS); con.setReadTimeout(READ_TIMEOUT_MS); // Forward a user-agent if you have one (optional). String ua = null; if (ctx != null && ctx.request != null && ctx.request.getHeaders() != null) { ua = ctx.request.getHeaders().get("user-agent"); } con.setRequestProperty("User-Agent", ua != null ? ua : "OAC-HttpsProxy/1.0"); int code = con.getResponseCode(); // Handle redirects manually to preserve content and avoid silent issues if (code == 301 || code == 302 || code == 303 || code == 307 || code == 308) { String location = con.getHeaderField("Location"); if (location == null || location.isBlank()) { return new WebResponsePacket( 502, "text/plain; charset=utf-8", Map.of(), ("Bad Gateway: redirect without Location (code=" + code + ")").getBytes(StandardCharsets.UTF_8) ); } // Resolve relative redirects URL resolved = new URL(target, location); con.disconnect(); return proxyGetInternal(ctx, resolved.toString(), depth + 1); } String contentType = con.getContentType(); if (contentType == null || contentType.isBlank()) { contentType = "application/octet-stream"; } byte[] body; try (InputStream in = (code >= 400 ? con.getErrorStream() : con.getInputStream())) { body = readAllBytes(in); } finally { con.disconnect(); } Map headers = new HashMap<>(); return new WebResponsePacket(code, contentType, headers, body); } private static byte[] readAllBytes(InputStream in) throws Exception { if (in == null) return new byte[0]; ByteArrayOutputStream out = new ByteArrayOutputStream(Math.max(1024, in.available())); byte[] buf = new byte[32 * 1024]; int r; while ((r = in.read(buf)) != -1) { out.write(buf, 0, r); } return out.toByteArray(); } }