1 package org
.asamk
.signal
.jsonrpc
;
3 import com
.fasterxml
.jackson
.core
.JsonParseException
;
4 import com
.fasterxml
.jackson
.databind
.JsonMappingException
;
5 import com
.fasterxml
.jackson
.databind
.JsonNode
;
6 import com
.fasterxml
.jackson
.databind
.ObjectMapper
;
7 import com
.fasterxml
.jackson
.databind
.node
.ContainerNode
;
8 import com
.fasterxml
.jackson
.databind
.node
.ValueNode
;
10 import org
.asamk
.signal
.util
.Util
;
11 import org
.slf4j
.Logger
;
12 import org
.slf4j
.LoggerFactory
;
14 import java
.io
.IOException
;
15 import java
.util
.Objects
;
16 import java
.util
.function
.Consumer
;
17 import java
.util
.function
.Supplier
;
18 import java
.util
.stream
.Collectors
;
19 import java
.util
.stream
.StreamSupport
;
21 public class JsonRpcReader
{
23 private final static Logger logger
= LoggerFactory
.getLogger(JsonRpcReader
.class);
25 private final JsonRpcSender jsonRpcSender
;
26 private final ObjectMapper objectMapper
;
27 private final Supplier
<String
> lineSupplier
;
30 final JsonRpcSender jsonRpcSender
, final Supplier
<String
> lineSupplier
32 this.jsonRpcSender
= jsonRpcSender
;
33 this.lineSupplier
= lineSupplier
;
34 this.objectMapper
= Util
.createJsonObjectMapper();
37 public void readRequests(
38 final RequestHandler requestHandler
, final Consumer
<JsonRpcResponse
> responseHandler
40 while (!Thread
.interrupted()) {
41 JsonRpcMessage message
= readMessage();
42 if (message
== null) break;
44 if (message
instanceof final JsonRpcRequest jsonRpcRequest
) {
45 logger
.debug("Received json rpc request, method: " + jsonRpcRequest
.method
);
46 final var response
= handleRequest(requestHandler
, jsonRpcRequest
);
47 if (response
!= null) {
48 jsonRpcSender
.sendResponse(response
);
50 } else if (message
instanceof JsonRpcResponse jsonRpcResponse
) {
51 responseHandler
.accept(jsonRpcResponse
);
53 final var responseList
= ((JsonRpcBatchMessage
) message
).getMessages().stream().map(jsonNode
-> {
54 final JsonRpcRequest request
;
56 request
= parseJsonRpcRequest(jsonNode
);
57 } catch (JsonRpcException e
) {
58 return JsonRpcResponse
.forError(e
.getError(), getId(jsonNode
));
61 return handleRequest(requestHandler
, request
);
62 }).filter(Objects
::nonNull
).collect(Collectors
.toList());
64 jsonRpcSender
.sendBatchResponses(responseList
);
69 private JsonRpcResponse
handleRequest(final RequestHandler requestHandler
, final JsonRpcRequest request
) {
71 final var result
= requestHandler
.apply(request
.getMethod(), request
.getParams());
72 if (request
.getId() != null) {
73 return JsonRpcResponse
.forSuccess(result
, request
.getId());
75 logger
.debug("Command '{}' succeeded but client didn't specify an id, dropping response",
78 } catch (JsonRpcException e
) {
79 if (request
.getId() != null) {
80 return JsonRpcResponse
.forError(e
.getError(), request
.getId());
82 logger
.debug("Command '{}' failed but client didn't specify an id, dropping error: {}",
90 private JsonRpcMessage
readMessage() {
91 while (!Thread
.interrupted()) {
92 String input
= lineSupplier
.get();
95 // Reached end of input stream
99 JsonRpcMessage message
= parseJsonRpcMessage(input
);
100 if (message
== null) continue;
108 private JsonRpcMessage
parseJsonRpcMessage(final String input
) {
109 final JsonNode jsonNode
;
111 jsonNode
= objectMapper
.readTree(input
);
112 } catch (JsonParseException e
) {
113 jsonRpcSender
.sendResponse(JsonRpcResponse
.forError(new JsonRpcResponse
.Error(JsonRpcResponse
.Error
.PARSE_ERROR
,
117 } catch (IOException e
) {
118 throw new AssertionError(e
);
121 if (jsonNode
== null) {
122 jsonRpcSender
.sendResponse(JsonRpcResponse
.forError(new JsonRpcResponse
.Error(JsonRpcResponse
.Error
.INVALID_REQUEST
,
126 } else if (jsonNode
.isArray()) {
127 if (jsonNode
.size() == 0) {
128 jsonRpcSender
.sendResponse(JsonRpcResponse
.forError(new JsonRpcResponse
.Error(JsonRpcResponse
.Error
.INVALID_REQUEST
,
133 return new JsonRpcBatchMessage(StreamSupport
.stream(jsonNode
.spliterator(), false)
134 .collect(Collectors
.toList()));
135 } else if (jsonNode
.isObject()) {
136 if (jsonNode
.has("result") || jsonNode
.has("error")) {
137 return parseJsonRpcResponse(jsonNode
);
140 return parseJsonRpcRequest(jsonNode
);
141 } catch (JsonRpcException e
) {
142 jsonRpcSender
.sendResponse(JsonRpcResponse
.forError(e
.getError(), getId(jsonNode
)));
147 jsonRpcSender
.sendResponse(JsonRpcResponse
.forError(new JsonRpcResponse
.Error(JsonRpcResponse
.Error
.INVALID_REQUEST
,
148 "unexpected type: " + jsonNode
.getNodeType().name(),
154 private ValueNode
getId(JsonNode jsonNode
) {
155 final var id
= jsonNode
.get("id");
156 return id
instanceof ValueNode ?
(ValueNode
) id
: null;
159 private JsonRpcRequest
parseJsonRpcRequest(final JsonNode input
) throws JsonRpcException
{
160 JsonRpcRequest request
;
162 request
= objectMapper
.treeToValue(input
, JsonRpcRequest
.class);
163 } catch (JsonMappingException e
) {
164 throw new JsonRpcException(new JsonRpcResponse
.Error(JsonRpcResponse
.Error
.INVALID_REQUEST
,
167 } catch (IOException e
) {
168 throw new AssertionError(e
);
171 if (!"2.0".equals(request
.getJsonrpc())) {
172 throw new JsonRpcException(new JsonRpcResponse
.Error(JsonRpcResponse
.Error
.INVALID_REQUEST
,
173 "only jsonrpc version 2.0 is supported",
177 if (request
.getMethod() == null) {
178 throw new JsonRpcException(new JsonRpcResponse
.Error(JsonRpcResponse
.Error
.INVALID_REQUEST
,
179 "method field must be set",
186 private JsonRpcResponse
parseJsonRpcResponse(final JsonNode input
) {
187 JsonRpcResponse response
;
189 response
= objectMapper
.treeToValue(input
, JsonRpcResponse
.class);
190 } catch (JsonParseException
| JsonMappingException e
) {
191 logger
.debug("Received invalid jsonrpc response {}", e
.getMessage());
193 } catch (IOException e
) {
194 throw new AssertionError(e
);
197 if (!"2.0".equals(response
.getJsonrpc())) {
198 logger
.debug("Received invalid jsonrpc response with invalid version {}", response
.getJsonrpc());
202 if (response
.getResult() != null && response
.getError() != null) {
203 logger
.debug("Received invalid jsonrpc response with both result and error");
207 if (response
.getResult() == null && response
.getError() == null) {
208 logger
.debug("Received invalid jsonrpc response without result and error");
212 if (response
.getId() == null || response
.getId().isNull()) {
213 logger
.debug("Received invalid jsonrpc response without id");
220 public interface RequestHandler
{
222 JsonNode
apply(String method
, ContainerNode
<?
> params
) throws JsonRpcException
;