X-Git-Url: https://git.nmode.ca/signal-cli/blobdiff_plain/81a11dc9776672e3468ee9a8eed556889fb2e070..fd851ba6cb39369f2cb1b5958a90cd023c05426a:/src/main/java/org/asamk/signal/jsonrpc/JsonRpcReader.java diff --git a/src/main/java/org/asamk/signal/jsonrpc/JsonRpcReader.java b/src/main/java/org/asamk/signal/jsonrpc/JsonRpcReader.java index 3bc7e701..27da9b0b 100644 --- a/src/main/java/org/asamk/signal/jsonrpc/JsonRpcReader.java +++ b/src/main/java/org/asamk/signal/jsonrpc/JsonRpcReader.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ContainerNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.ValueNode; import org.asamk.signal.util.Util; @@ -12,10 +13,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; +import java.io.InputStream; import java.util.Objects; import java.util.function.Consumer; import java.util.function.Supplier; -import java.util.stream.Collectors; import java.util.stream.StreamSupport; public class JsonRpcReader { @@ -24,41 +25,77 @@ public class JsonRpcReader { private final JsonRpcSender jsonRpcSender; private final ObjectMapper objectMapper; + private final InputStream input; private final Supplier lineSupplier; - public JsonRpcReader( - final JsonRpcSender jsonRpcSender, final Supplier lineSupplier - ) { + public JsonRpcReader(final JsonRpcSender jsonRpcSender, final Supplier lineSupplier) { this.jsonRpcSender = jsonRpcSender; + this.input = null; this.lineSupplier = lineSupplier; this.objectMapper = Util.createJsonObjectMapper(); } + public JsonRpcReader(final JsonRpcSender jsonRpcSender, final InputStream input) { + this.jsonRpcSender = jsonRpcSender; + this.input = input; + this.lineSupplier = null; + this.objectMapper = Util.createJsonObjectMapper(); + } + public void readMessages(final RequestHandler requestHandler, final Consumer responseHandler) { + if (input != null) { + JsonRpcMessage message = parseJsonRpcMessage(input); + if (message == null) { + return; + } + + handleMessage(message, requestHandler, responseHandler); + return; + } + while (!Thread.interrupted()) { - JsonRpcMessage message = readMessage(); - if (message == null) break; - - if (message instanceof final JsonRpcRequest jsonRpcRequest) { - logger.debug("Received json rpc request, method: " + jsonRpcRequest.method); - final var response = handleRequest(requestHandler, jsonRpcRequest); - if (response != null) { - jsonRpcSender.sendResponse(response); + String input = lineSupplier.get(); + if (input == null) { + logger.trace("Reached end of JSON-RPC input stream."); + break; + } + + logger.trace("Incoming JSON-RPC message: {}", input); + JsonRpcMessage message = parseJsonRpcMessage(input); + if (message == null) { + continue; + } + + handleMessage(message, requestHandler, responseHandler); + } + } + + private void handleMessage( + final JsonRpcMessage message, + final RequestHandler requestHandler, + final Consumer responseHandler + ) { + if (message instanceof final JsonRpcRequest jsonRpcRequest) { + logger.debug("Received json rpc request, method: " + jsonRpcRequest.getMethod()); + final var response = handleRequest(requestHandler, jsonRpcRequest); + if (response != null) { + jsonRpcSender.sendResponse(response); + } + } else if (message instanceof JsonRpcResponse jsonRpcResponse) { + responseHandler.accept(jsonRpcResponse); + } else { + final var responseList = ((JsonRpcBatchMessage) message).getMessages().stream().map(jsonNode -> { + final JsonRpcRequest request; + try { + request = parseJsonRpcRequest(jsonNode); + } catch (JsonRpcException e) { + return JsonRpcResponse.forError(e.getError(), getId(jsonNode)); } - } else if (message instanceof JsonRpcResponse jsonRpcResponse) { - responseHandler.accept(jsonRpcResponse); - } else { - final var responseList = ((JsonRpcBatchMessage) message).getMessages().stream().map(jsonNode -> { - final JsonRpcRequest request; - try { - request = parseJsonRpcRequest(jsonNode); - } catch (JsonRpcException e) { - return JsonRpcResponse.forError(e.getError(), getId(jsonNode)); - } - return handleRequest(requestHandler, request); - }).filter(Objects::nonNull).collect(Collectors.toList()); + return handleRequest(requestHandler, request); + }).filter(Objects::nonNull).toList(); + if (responseList.size() > 0) { jsonRpcSender.sendBatchResponses(responseList); } } @@ -85,25 +122,23 @@ public class JsonRpcReader { return null; } - private JsonRpcMessage readMessage() { - while (!Thread.interrupted()) { - String input = lineSupplier.get(); - - if (input == null) { - // Reached end of input stream - break; - } - - JsonRpcMessage message = parseJsonRpcMessage(input); - if (message == null) continue; - - return message; + private JsonRpcMessage parseJsonRpcMessage(final String input) { + final JsonNode jsonNode; + try { + jsonNode = objectMapper.readTree(input); + } catch (JsonParseException e) { + jsonRpcSender.sendResponse(JsonRpcResponse.forError(new JsonRpcResponse.Error(JsonRpcResponse.Error.PARSE_ERROR, + e.getMessage(), + null), null)); + return null; + } catch (IOException e) { + throw new AssertionError(e); } - return null; + return parseJsonRpcMessage(jsonNode); } - private JsonRpcMessage parseJsonRpcMessage(final String input) { + private JsonRpcMessage parseJsonRpcMessage(final InputStream input) { final JsonNode jsonNode; try { jsonNode = objectMapper.readTree(input); @@ -116,6 +151,10 @@ public class JsonRpcReader { throw new AssertionError(e); } + return parseJsonRpcMessage(jsonNode); + } + + private JsonRpcMessage parseJsonRpcMessage(final JsonNode jsonNode) { if (jsonNode == null) { jsonRpcSender.sendResponse(JsonRpcResponse.forError(new JsonRpcResponse.Error(JsonRpcResponse.Error.INVALID_REQUEST, "invalid request", @@ -128,8 +167,7 @@ public class JsonRpcReader { null), null)); return null; } - return new JsonRpcBatchMessage(StreamSupport.stream(jsonNode.spliterator(), false) - .collect(Collectors.toList())); + return new JsonRpcBatchMessage(StreamSupport.stream(jsonNode.spliterator(), false).toList()); } else if (jsonNode.isObject()) { if (jsonNode.has("result") || jsonNode.has("error")) { return parseJsonRpcResponse(jsonNode); @@ -155,6 +193,10 @@ public class JsonRpcReader { } private JsonRpcRequest parseJsonRpcRequest(final JsonNode input) throws JsonRpcException { + if (input instanceof ObjectNode i && input.has("params") && input.get("params").isNull()) { + // Workaround for clients that send a null params field instead of omitting it + i.remove("params"); + } JsonRpcRequest request; try { request = objectMapper.treeToValue(input, JsonRpcRequest.class);