2025-04-10 14:42:53 +02:00

124 lines
5.0 KiB
Java

package eu.starsong.ghidra.util;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.sun.net.httpserver.HttpExchange;
import eu.starsong.ghidra.api.ResponseBuilder; // Use the ResponseBuilder
import ghidra.util.Msg;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
public class HttpUtil {
private static final Gson gson = new Gson();
/**
* Sends a JSON response with the given status code.
* Uses the ResponseBuilder internally.
*/
public static void sendJsonResponse(HttpExchange exchange, JsonObject jsonObj, int statusCode, int port) throws IOException {
try {
String json = gson.toJson(jsonObj);
byte[] bytes = json.getBytes(StandardCharsets.UTF_8);
exchange.getResponseHeaders().set("Content-Type", "application/json; charset=utf-8");
// Consider adding CORS headers if needed:
// exchange.getResponseHeaders().set("Access-Control-Allow-Origin", "*");
long responseLength = (statusCode == 204) ? -1 : bytes.length;
exchange.sendResponseHeaders(statusCode, responseLength);
if (responseLength != -1) {
try (OutputStream os = exchange.getResponseBody()) {
os.write(bytes);
}
} else {
exchange.getResponseBody().close(); // Important for 204
}
} catch (Exception e) {
Msg.error(HttpUtil.class, "Error sending JSON response: " + e.getMessage(), e);
// Avoid sending another error response here to prevent potential loops
if (!exchange.getResponseHeaders().containsKey("Content-Type")) {
byte[] errorBytes = ("Internal Server Error: " + e.getMessage()).getBytes(StandardCharsets.UTF_8);
exchange.getResponseHeaders().set("Content-Type", "text/plain; charset=utf-8");
exchange.sendResponseHeaders(500, errorBytes.length);
try (OutputStream os = exchange.getResponseBody()) {
os.write(errorBytes);
} catch (IOException writeErr) {
Msg.error(HttpUtil.class, "Failed to send even plain text error response", writeErr);
}
}
throw new IOException("Failed to send JSON response", e);
}
}
/**
* Sends a standardized error response using ResponseBuilder.
*/
public static void sendErrorResponse(HttpExchange exchange, int statusCode, String message, String errorCode, int port) throws IOException {
ResponseBuilder builder = new ResponseBuilder(exchange, port)
.success(false)
.error(message, errorCode);
sendJsonResponse(exchange, builder.build(), statusCode, port);
}
/**
* Parses query parameters from the URL.
*/
public static Map<String, String> parseQueryParams(HttpExchange exchange) {
Map<String, String> result = new HashMap<>();
String query = exchange.getRequestURI().getQuery();
if (query != null) {
String[] pairs = query.split("&");
for (String p : pairs) {
String[] kv = p.split("=");
if (kv.length == 2) {
try {
result.put(kv[0], java.net.URLDecoder.decode(kv[1], StandardCharsets.UTF_8));
} catch (Exception e) {
Msg.warn(HttpUtil.class, "Failed to decode query parameter: " + kv[0]);
result.put(kv[0], kv[1]);
}
} else if (kv.length == 1 && !kv[0].isEmpty()) {
result.put(kv[0], "");
}
}
}
return result;
}
/**
* Parses POST body parameters strictly as JSON.
*/
public static Map<String, String> parseJsonPostParams(HttpExchange exchange) throws IOException {
byte[] body = exchange.getRequestBody().readAllBytes();
String bodyStr = new String(body, StandardCharsets.UTF_8);
Map<String, String> params = new HashMap<>();
try {
JsonObject json = gson.fromJson(bodyStr, JsonObject.class);
if (json == null) {
return params;
}
for (Map.Entry<String, JsonElement> entry : json.entrySet()) {
String key = entry.getKey();
JsonElement value = entry.getValue();
if (value.isJsonPrimitive()) {
params.put(key, value.getAsString());
} else {
params.put(key, value.toString()); // Stringify non-primitives
}
}
} catch (Exception e) {
Msg.error(HttpUtil.class, "Failed to parse JSON request body: " + bodyStr, e);
throw new IOException("Invalid JSON request body: " + e.getMessage(), e);
}
return params;
}
}