Switch http server to use command handler

This commit is contained in:
cedb 2022-11-01 15:11:40 -04:00
parent 11179c672f
commit 7111cad402
2 changed files with 35 additions and 44 deletions

View file

@ -4,20 +4,19 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpServer; import com.sun.net.httpserver.HttpServer;
import org.asamk.signal.commands.Commands;
import org.asamk.signal.jsonrpc.JsonRpcReader;
import org.asamk.signal.jsonrpc.JsonRpcResponse; import org.asamk.signal.jsonrpc.JsonRpcResponse;
import org.asamk.signal.jsonrpc.SignalJsonRpcDispatcherHandler; import org.asamk.signal.jsonrpc.JsonRpcSender;
import org.asamk.signal.jsonrpc.SignalJsonRpcCommandHandler;
import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.Manager;
import org.asamk.signal.manager.MultiAccountManager; import org.asamk.signal.manager.MultiAccountManager;
import org.asamk.signal.output.JsonWriter;
import org.asamk.signal.util.IOUtils;
import org.asamk.signal.util.Util; import org.asamk.signal.util.Util;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.io.IOException; import java.io.IOException;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.LinkedList;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
public class HttpServerHandler { public class HttpServerHandler {
@ -53,53 +52,40 @@ public class HttpServerHandler {
final var server = HttpServer.create(new InetSocketAddress(port), 0); final var server = HttpServer.create(new InetSocketAddress(port), 0);
server.setExecutor(Executors.newFixedThreadPool(10)); server.setExecutor(Executors.newFixedThreadPool(10));
server.createContext("/json-rpc", httpExchange -> { server.createContext("/api/v1/rpc", httpExchange -> {
try { try {
if (!"POST".equals(httpExchange.getRequestMethod())) { if (!"POST".equals(httpExchange.getRequestMethod())) {
throw new HttpServerException(405, "Method not supported."); throw new HttpServerException(405, "Method not supported.");
} }
// Create a custom writer which receives our response final SignalJsonRpcCommandHandler commandHandler;
final var valueHolder = new Object[] { null };
final JsonWriter jsonWriter = (Object o) -> valueHolder[0] = o;
// This queue is used to deliver our request and then deliver a null
// value which terminates the reading process
final var stringRequest = IOUtils.readAll(httpExchange.getRequestBody(), StandardCharsets.UTF_8);
final var queue = new LinkedList<String>();
queue.addLast(stringRequest);
queue.addLast(null);
// Create dispatcher and handle connection
// Right now we are creating a new one for every request. This may not be
// totally efficient, but it handles possible issues that would arise with
// multithreading.
final var dispatcher = new SignalJsonRpcDispatcherHandler(
jsonWriter,
queue::removeFirst,
true
);
if (c != null) { if (c != null) {
dispatcher.handleConnection(c); commandHandler = new SignalJsonRpcCommandHandler(c, Commands::getCommand);
} else { } else {
dispatcher.handleConnection(m); commandHandler = new SignalJsonRpcCommandHandler(m, Commands::getCommand);
} }
// Extract and process the response final Object[] result = {null};
final var response = valueHolder[0] != null ? valueHolder[0] : JsonRpcResponse.forSuccess(null, null); final var jsonRpcSender = new JsonRpcSender(s -> {
if (result[0] != null) {
throw new AssertionError("There should only be a single JSON-RPC response");
}
if (response instanceof JsonRpcResponse jsonRpcResponse) { result[0] = s;
if (jsonRpcResponse.getError() == null) { });
sendResponse(200, jsonRpcResponse, httpExchange);
final var jsonRpcReader = new JsonRpcReader(jsonRpcSender, httpExchange.getRequestBody());
jsonRpcReader.readMessages((method, params) -> commandHandler.handleRequest(objectMapper, method, params),
response -> logger.debug("Received unexpected response for id {}", response.getId()));
if (result[0] !=null) {
sendResponse(200, result[0], httpExchange);
} else { } else {
sendResponse(500, jsonRpcResponse, httpExchange); sendResponse(201, null, httpExchange);
}
} else {
logger.error("Invalid response object was received." + response);
throw new HttpServerException(500, "An internal server error has occurred.");
} }
} }
catch (HttpServerException aEx) { catch (HttpServerException aEx) {
logger.error("Failed to process request.", aEx); logger.error("Failed to process request.", aEx);
@ -109,7 +95,7 @@ public class HttpServerHandler {
} }
catch (Throwable aEx) { catch (Throwable aEx) {
logger.error("Failed to process request.", aEx); logger.error("Failed to process request.", aEx);
sendResponse(500, JsonRpcResponse.forError( sendResponse(200, JsonRpcResponse.forError(
new JsonRpcResponse.Error(JsonRpcResponse.Error.INTERNAL_ERROR, new JsonRpcResponse.Error(JsonRpcResponse.Error.INTERNAL_ERROR,
"An internal server error has occurred.", null), null), httpExchange); "An internal server error has occurred.", null), null), httpExchange);
} }
@ -123,10 +109,15 @@ public class HttpServerHandler {
} }
private void sendResponse(int status, JsonRpcResponse response, HttpExchange httpExchange) throws IOException { private void sendResponse(int status, Object response, HttpExchange httpExchange) throws IOException {
if (response != null) {
final var byteResponse = objectMapper.writeValueAsBytes(response); final var byteResponse = objectMapper.writeValueAsBytes(response);
httpExchange.sendResponseHeaders(status, byteResponse.length); httpExchange.sendResponseHeaders(status, byteResponse.length);
httpExchange.getResponseBody().write(byteResponse); httpExchange.getResponseBody().write(byteResponse);
} else {
httpExchange.sendResponseHeaders(status, 0);
}
httpExchange.getResponseBody().close(); httpExchange.getResponseBody().close();
} }

View file

@ -52,7 +52,7 @@ public class SignalJsonRpcCommandHandler {
this.commandProvider = commandProvider; this.commandProvider = commandProvider;
} }
JsonNode handleRequest( public JsonNode handleRequest(
final ObjectMapper objectMapper, final String method, ContainerNode<?> params final ObjectMapper objectMapper, final String method, ContainerNode<?> params
) throws JsonRpcException { ) throws JsonRpcException {
var command = getCommand(method); var command = getCommand(method);