Added HttpsProxy for resolving Websites from the WWW
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
package org.openautonomousconnection.webserver.api;
|
||||
|
||||
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.
|
||||
*
|
||||
* <p>Limitations:
|
||||
* <ul>
|
||||
* <li>Does not rewrite HTML/CSS URLs. If you need full offline/proxied subresources,
|
||||
* implement URL rewriting and route subresource paths through this proxy as well.</li>
|
||||
* </ul>
|
||||
*/
|
||||
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<String, String> 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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user