1 package org
.asamk
.signal
.http
;
3 import com
.fasterxml
.jackson
.databind
.ObjectMapper
;
4 import com
.sun
.net
.httpserver
.HttpExchange
;
5 import com
.sun
.net
.httpserver
.HttpServer
;
7 import org
.asamk
.signal
.commands
.Commands
;
8 import org
.asamk
.signal
.jsonrpc
.JsonRpcReader
;
9 import org
.asamk
.signal
.jsonrpc
.JsonRpcResponse
;
10 import org
.asamk
.signal
.jsonrpc
.JsonRpcSender
;
11 import org
.asamk
.signal
.jsonrpc
.SignalJsonRpcCommandHandler
;
12 import org
.asamk
.signal
.manager
.Manager
;
13 import org
.asamk
.signal
.manager
.MultiAccountManager
;
14 import org
.asamk
.signal
.util
.Util
;
15 import org
.slf4j
.Logger
;
16 import org
.slf4j
.LoggerFactory
;
18 import java
.io
.IOException
;
19 import java
.net
.InetSocketAddress
;
20 import java
.util
.concurrent
.Executors
;
22 public class HttpServerHandler
{
24 private final static Logger logger
= LoggerFactory
.getLogger(HttpServerHandler
.class);
26 private final ObjectMapper objectMapper
= Util
.createJsonObjectMapper();
28 private final InetSocketAddress address
;
30 private final SignalJsonRpcCommandHandler commandHandler
;
32 public HttpServerHandler(final InetSocketAddress address
, final Manager m
) {
33 this.address
= address
;
34 commandHandler
= new SignalJsonRpcCommandHandler(m
, Commands
::getCommand
);
37 public HttpServerHandler(final InetSocketAddress address
, final MultiAccountManager c
) {
38 this.address
= address
;
39 commandHandler
= new SignalJsonRpcCommandHandler(c
, Commands
::getCommand
);
42 public void init() throws IOException
{
43 logger
.info("Starting server on " + address
.toString());
45 final var server
= HttpServer
.create(address
, 0);
46 server
.setExecutor(Executors
.newFixedThreadPool(10));
48 server
.createContext("/api/v1/rpc", this::handleRpcEndpoint
);
53 private void sendResponse(int status
, Object response
, HttpExchange httpExchange
) throws IOException
{
54 if (response
!= null) {
55 final var byteResponse
= objectMapper
.writeValueAsBytes(response
);
57 httpExchange
.getResponseHeaders().add("Content-Type", "application/json");
58 httpExchange
.sendResponseHeaders(status
, byteResponse
.length
);
60 httpExchange
.getResponseBody().write(byteResponse
);
62 httpExchange
.sendResponseHeaders(status
, -1);
65 httpExchange
.getResponseBody().close();
68 private void handleRpcEndpoint(HttpExchange httpExchange
) throws IOException
{
69 if (!"/api/v1/rpc".equals(httpExchange
.getRequestURI().getPath())) {
70 sendResponse(404, null, httpExchange
);
73 if (!"POST".equals(httpExchange
.getRequestMethod())) {
74 sendResponse(405, null, httpExchange
);
78 if (!"application/json".equals(httpExchange
.getRequestHeaders().getFirst("Content-Type"))) {
79 sendResponse(415, null, httpExchange
);
85 final Object
[] result
= {null};
86 final var jsonRpcSender
= new JsonRpcSender(s
-> {
87 if (result
[0] != null) {
88 throw new AssertionError("There should only be a single JSON-RPC response");
94 final var jsonRpcReader
= new JsonRpcReader(jsonRpcSender
, httpExchange
.getRequestBody());
95 jsonRpcReader
.readMessages((method
, params
) -> commandHandler
.handleRequest(objectMapper
, method
, params
),
96 response
-> logger
.debug("Received unexpected response for id {}", response
.getId()));
98 if (result
[0] != null) {
99 sendResponse(200, result
[0], httpExchange
);
101 sendResponse(201, null, httpExchange
);
104 } catch (Throwable aEx
) {
105 logger
.error("Failed to process request.", aEx
);
107 JsonRpcResponse
.forError(new JsonRpcResponse
.Error(JsonRpcResponse
.Error
.INTERNAL_ERROR
,
108 "An internal server error has occurred.",