]> nmode's Git Repositories - signal-cli/blob - src/main/java/org/asamk/signal/http/HttpServerHandler.java
Add check for exact path match
[signal-cli] / src / main / java / org / asamk / signal / http / HttpServerHandler.java
1 package org.asamk.signal.http;
2
3 import com.fasterxml.jackson.databind.ObjectMapper;
4 import com.sun.net.httpserver.HttpExchange;
5 import com.sun.net.httpserver.HttpServer;
6
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;
17
18 import java.io.IOException;
19 import java.net.InetSocketAddress;
20 import java.util.concurrent.Executors;
21
22 public class HttpServerHandler {
23
24 private final static Logger logger = LoggerFactory.getLogger(HttpServerHandler.class);
25
26 private final ObjectMapper objectMapper = Util.createJsonObjectMapper();
27
28 private final InetSocketAddress address;
29
30 private final SignalJsonRpcCommandHandler commandHandler;
31
32 public HttpServerHandler(final InetSocketAddress address, final Manager m) {
33 this.address = address;
34 commandHandler = new SignalJsonRpcCommandHandler(m, Commands::getCommand);
35 }
36
37 public HttpServerHandler(final InetSocketAddress address, final MultiAccountManager c) {
38 this.address = address;
39 commandHandler = new SignalJsonRpcCommandHandler(c, Commands::getCommand);
40 }
41
42 public void init() throws IOException {
43 logger.info("Starting server on " + address.toString());
44
45 final var server = HttpServer.create(address, 0);
46 server.setExecutor(Executors.newFixedThreadPool(10));
47
48 server.createContext("/api/v1/rpc", this::handleRpcEndpoint);
49
50 server.start();
51 }
52
53 private void sendResponse(int status, Object response, HttpExchange httpExchange) throws IOException {
54 if (response != null) {
55 final var byteResponse = objectMapper.writeValueAsBytes(response);
56
57 httpExchange.getResponseHeaders().add("Content-Type", "application/json");
58 httpExchange.sendResponseHeaders(status, byteResponse.length);
59
60 httpExchange.getResponseBody().write(byteResponse);
61 } else {
62 httpExchange.sendResponseHeaders(status, -1);
63 }
64
65 httpExchange.getResponseBody().close();
66 }
67
68 private void handleRpcEndpoint(HttpExchange httpExchange) throws IOException {
69 if (!"/api/v1/rpc".equals(httpExchange.getRequestURI().getPath())) {
70 sendResponse(404, null, httpExchange);
71 return;
72 }
73 if (!"POST".equals(httpExchange.getRequestMethod())) {
74 sendResponse(405, null, httpExchange);
75 return;
76 }
77
78 if (!"application/json".equals(httpExchange.getRequestHeaders().getFirst("Content-Type"))) {
79 sendResponse(415, null, httpExchange);
80 return;
81 }
82
83 try {
84
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");
89 }
90
91 result[0] = s;
92 });
93
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()));
97
98 if (result[0] != null) {
99 sendResponse(200, result[0], httpExchange);
100 } else {
101 sendResponse(201, null, httpExchange);
102 }
103
104 } catch (Throwable aEx) {
105 logger.error("Failed to process request.", aEx);
106 sendResponse(200,
107 JsonRpcResponse.forError(new JsonRpcResponse.Error(JsonRpcResponse.Error.INTERNAL_ERROR,
108 "An internal server error has occurred.",
109 null), null),
110 httpExchange);
111 }
112 }
113 }