Fix project info endpoints with simpler JSON generation
Simplify JSON response generation for better reliability. Both root and info endpoints now use direct string building instead of the JSON library.
This commit is contained in:
parent
5cf8f5fb16
commit
a615813e2d
@ -146,26 +146,56 @@ def register_instance(port: int, url: str = None) -> str:
|
|||||||
project_info = {"url": url}
|
project_info = {"url": url}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
# Try the root endpoint first
|
||||||
|
root_url = f"{url}/"
|
||||||
|
print(f"Trying to get root info from {root_url}", file=sys.stderr)
|
||||||
|
root_response = requests.get(root_url, timeout=1.5) # Short timeout for root
|
||||||
|
|
||||||
|
if root_response.ok:
|
||||||
|
try:
|
||||||
|
print(f"Got response from root: {root_response.text}", file=sys.stderr)
|
||||||
|
root_data = root_response.json()
|
||||||
|
|
||||||
|
# Extract basic information from root
|
||||||
|
if "project" in root_data and root_data["project"]:
|
||||||
|
project_info["project"] = root_data["project"]
|
||||||
|
if "program" in root_data and root_data["program"]:
|
||||||
|
project_info["file"] = root_data["program"]
|
||||||
|
if "programID" in root_data and root_data["programID"]:
|
||||||
|
project_info["program_id"] = root_data["programID"]
|
||||||
|
|
||||||
|
print(f"Root data parsed: {project_info}", file=sys.stderr)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error parsing root info: {e}", file=sys.stderr)
|
||||||
|
else:
|
||||||
|
print(f"Root endpoint returned {root_response.status_code}", file=sys.stderr)
|
||||||
|
|
||||||
|
# If we don't have project info yet, try the /info endpoint as a fallback
|
||||||
|
if not project_info.get("project") and not project_info.get("file"):
|
||||||
info_url = f"{url}/info"
|
info_url = f"{url}/info"
|
||||||
|
print(f"Trying fallback info from {info_url}", file=sys.stderr)
|
||||||
|
|
||||||
|
try:
|
||||||
info_response = requests.get(info_url, timeout=2)
|
info_response = requests.get(info_url, timeout=2)
|
||||||
if info_response.ok:
|
if info_response.ok:
|
||||||
try:
|
try:
|
||||||
# Parse JSON response
|
|
||||||
info_data = info_response.json()
|
info_data = info_response.json()
|
||||||
|
|
||||||
# Extract relevant information
|
# Extract relevant information
|
||||||
project_info["project"] = info_data.get("project", "Unknown")
|
if "project" in info_data and info_data["project"]:
|
||||||
|
project_info["project"] = info_data["project"]
|
||||||
|
|
||||||
# Handle file information which is nested
|
# Handle file information
|
||||||
file_info = info_data.get("file", {})
|
file_info = info_data.get("file", {})
|
||||||
if file_info:
|
if isinstance(file_info, dict) and file_info.get("name"):
|
||||||
project_info["file"] = file_info.get("name", "")
|
project_info["file"] = file_info.get("name", "")
|
||||||
project_info["path"] = file_info.get("path", "")
|
project_info["path"] = file_info.get("path", "")
|
||||||
project_info["architecture"] = file_info.get("architecture", "")
|
project_info["architecture"] = file_info.get("architecture", "")
|
||||||
project_info["endian"] = file_info.get("endian", "")
|
project_info["endian"] = file_info.get("endian", "")
|
||||||
except ValueError:
|
print(f"Info data parsed: {project_info}", file=sys.stderr)
|
||||||
# Not valid JSON
|
except Exception as e:
|
||||||
pass
|
print(f"Error parsing info endpoint: {e}", file=sys.stderr)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error connecting to info endpoint: {e}", file=sys.stderr)
|
||||||
except Exception:
|
except Exception:
|
||||||
# Non-critical, continue with registration even if project info fails
|
# Non-critical, continue with registration even if project info fails
|
||||||
pass
|
pass
|
||||||
|
|||||||
@ -213,21 +213,99 @@ public class GhydraMCPPlugin extends Plugin implements ApplicationLevelPlugin {
|
|||||||
sendResponse(exchange, sb.toString());
|
sendResponse(exchange, sb.toString());
|
||||||
});
|
});
|
||||||
|
|
||||||
// Info endpoints - both root and /info for flexibility
|
// Super simple info endpoint with guaranteed response
|
||||||
server.createContext("/info", exchange -> {
|
server.createContext("/info", exchange -> {
|
||||||
if ("GET".equals(exchange.getRequestMethod())) {
|
try {
|
||||||
sendJsonResponse(exchange, getProjectInfo());
|
String response = "{\n";
|
||||||
} else {
|
response += "\"port\": " + port + ",\n";
|
||||||
exchange.sendResponseHeaders(405, -1); // Method Not Allowed
|
response += "\"isBaseInstance\": " + isBaseInstance + ",\n";
|
||||||
|
|
||||||
|
// Try to get program info if available
|
||||||
|
Program program = getCurrentProgram();
|
||||||
|
String programName = "\"\"";
|
||||||
|
if (program != null) {
|
||||||
|
programName = "\"" + program.getName() + "\"";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to get project info if available
|
||||||
|
Project project = tool.getProject();
|
||||||
|
String projectName = "\"\"";
|
||||||
|
if (project != null) {
|
||||||
|
projectName = "\"" + project.getName() + "\"";
|
||||||
|
}
|
||||||
|
|
||||||
|
response += "\"project\": " + projectName + ",\n";
|
||||||
|
response += "\"file\": " + programName + "\n";
|
||||||
|
response += "}";
|
||||||
|
|
||||||
|
Msg.info(this, "Sending /info response: " + response);
|
||||||
|
byte[] bytes = response.getBytes(StandardCharsets.UTF_8);
|
||||||
|
exchange.getResponseHeaders().set("Content-Type", "application/json; charset=utf-8");
|
||||||
|
exchange.sendResponseHeaders(200, bytes.length);
|
||||||
|
try (OutputStream os = exchange.getResponseBody()) {
|
||||||
|
os.write(bytes);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Msg.error(this, "Error serving /info endpoint", e);
|
||||||
|
try {
|
||||||
|
String error = "{\"error\": \"Internal error\", \"port\": " + port + "}";
|
||||||
|
byte[] bytes = error.getBytes(StandardCharsets.UTF_8);
|
||||||
|
exchange.getResponseHeaders().set("Content-Type", "application/json; charset=utf-8");
|
||||||
|
exchange.sendResponseHeaders(200, bytes.length);
|
||||||
|
try (OutputStream os = exchange.getResponseBody()) {
|
||||||
|
os.write(bytes);
|
||||||
|
}
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
Msg.error(this, "Failed to send error response", ioe);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Root endpoint also returns project info
|
// Super simple root endpoint - exact same as /info for consistency
|
||||||
server.createContext("/", exchange -> {
|
server.createContext("/", exchange -> {
|
||||||
if ("GET".equals(exchange.getRequestMethod())) {
|
try {
|
||||||
sendJsonResponse(exchange, getProjectInfo());
|
String response = "{\n";
|
||||||
} else {
|
response += "\"port\": " + port + ",\n";
|
||||||
exchange.sendResponseHeaders(405, -1); // Method Not Allowed
|
response += "\"isBaseInstance\": " + isBaseInstance + ",\n";
|
||||||
|
|
||||||
|
// Try to get program info if available
|
||||||
|
Program program = getCurrentProgram();
|
||||||
|
String programName = "\"\"";
|
||||||
|
if (program != null) {
|
||||||
|
programName = "\"" + program.getName() + "\"";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to get project info if available
|
||||||
|
Project project = tool.getProject();
|
||||||
|
String projectName = "\"\"";
|
||||||
|
if (project != null) {
|
||||||
|
projectName = "\"" + project.getName() + "\"";
|
||||||
|
}
|
||||||
|
|
||||||
|
response += "\"project\": " + projectName + ",\n";
|
||||||
|
response += "\"file\": " + programName + "\n";
|
||||||
|
response += "}";
|
||||||
|
|
||||||
|
Msg.info(this, "Sending / response: " + response);
|
||||||
|
byte[] bytes = response.getBytes(StandardCharsets.UTF_8);
|
||||||
|
exchange.getResponseHeaders().set("Content-Type", "application/json; charset=utf-8");
|
||||||
|
exchange.sendResponseHeaders(200, bytes.length);
|
||||||
|
try (OutputStream os = exchange.getResponseBody()) {
|
||||||
|
os.write(bytes);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Msg.error(this, "Error serving / endpoint", e);
|
||||||
|
try {
|
||||||
|
String error = "{\"error\": \"Internal error\", \"port\": " + port + "}";
|
||||||
|
byte[] bytes = error.getBytes(StandardCharsets.UTF_8);
|
||||||
|
exchange.getResponseHeaders().set("Content-Type", "application/json; charset=utf-8");
|
||||||
|
exchange.sendResponseHeaders(200, bytes.length);
|
||||||
|
try (OutputStream os = exchange.getResponseBody()) {
|
||||||
|
os.write(bytes);
|
||||||
|
}
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
Msg.error(this, "Failed to send error response", ioe);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -569,56 +647,32 @@ public class GhydraMCPPlugin extends Plugin implements ApplicationLevelPlugin {
|
|||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Program getCurrentProgram() {
|
|
||||||
ProgramManager pm = tool.getService(ProgramManager.class);
|
|
||||||
return pm != null ? pm.getCurrentProgram() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get information about the current project and open file in JSON format
|
* Get the current program from the tool
|
||||||
*/
|
*/
|
||||||
private JSONObject getProjectInfo() {
|
public Program getCurrentProgram() {
|
||||||
JSONObject info = new JSONObject();
|
if (tool == null) {
|
||||||
Program program = getCurrentProgram();
|
Msg.debug(this, "Tool is null when trying to get current program");
|
||||||
|
return null;
|
||||||
// Get project information if available
|
|
||||||
Project project = tool.getProject();
|
|
||||||
if (project != null) {
|
|
||||||
info.put("project", project.getName());
|
|
||||||
} else {
|
|
||||||
info.put("project", "Unknown");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create file information object
|
try {
|
||||||
JSONObject fileInfo = new JSONObject();
|
ProgramManager pm = tool.getService(ProgramManager.class);
|
||||||
|
if (pm == null) {
|
||||||
// Get current file information if available
|
Msg.debug(this, "ProgramManager service is not available");
|
||||||
if (program != null) {
|
return null;
|
||||||
// Basic info
|
|
||||||
fileInfo.put("name", program.getName());
|
|
||||||
|
|
||||||
// Try to get more detailed info
|
|
||||||
DomainFile domainFile = program.getDomainFile();
|
|
||||||
if (domainFile != null) {
|
|
||||||
fileInfo.put("path", domainFile.getPathname());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add any additional file info we might want
|
Program program = pm.getCurrentProgram();
|
||||||
fileInfo.put("architecture", program.getLanguage().getProcessor().toString());
|
Msg.debug(this, "Got current program: " + (program != null ? program.getName() : "null"));
|
||||||
fileInfo.put("endian", program.getLanguage().isBigEndian() ? "big" : "little");
|
return program;
|
||||||
|
}
|
||||||
info.put("file", fileInfo);
|
catch (Exception e) {
|
||||||
} else {
|
Msg.error(this, "Error getting current program", e);
|
||||||
info.put("file", null);
|
return null;
|
||||||
info.put("status", "No file open");
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add server metadata
|
|
||||||
info.put("port", port);
|
|
||||||
info.put("isBaseInstance", isBaseInstance);
|
|
||||||
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void sendResponse(HttpExchange exchange, String response) throws IOException {
|
private void sendResponse(HttpExchange exchange, String response) throws IOException {
|
||||||
byte[] bytes = response.getBytes(StandardCharsets.UTF_8);
|
byte[] bytes = response.getBytes(StandardCharsets.UTF_8);
|
||||||
@ -629,18 +683,6 @@ public class GhydraMCPPlugin extends Plugin implements ApplicationLevelPlugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Send a JSON response to the client
|
|
||||||
*/
|
|
||||||
private void sendJsonResponse(HttpExchange exchange, JSONObject json) throws IOException {
|
|
||||||
String jsonString = json.toJSONString();
|
|
||||||
byte[] bytes = jsonString.getBytes(StandardCharsets.UTF_8);
|
|
||||||
exchange.getResponseHeaders().set("Content-Type", "application/json; charset=utf-8");
|
|
||||||
exchange.sendResponseHeaders(200, bytes.length);
|
|
||||||
try (OutputStream os = exchange.getResponseBody()) {
|
|
||||||
os.write(bytes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int findAvailablePort() {
|
private int findAvailablePort() {
|
||||||
int basePort = 8192;
|
int basePort = 8192;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user