mirror of
https://github.com/AsamK/signal-cli
synced 2025-09-02 04:20:38 +00:00
Add initial proof of concept for http server
This commit is contained in:
parent
49aaff2bbe
commit
0338befcf3
4 changed files with 153 additions and 1 deletions
|
@ -16,6 +16,7 @@ public class Commands {
|
||||||
addCommand(new DeleteLocalAccountDataCommand());
|
addCommand(new DeleteLocalAccountDataCommand());
|
||||||
addCommand(new FinishLinkCommand());
|
addCommand(new FinishLinkCommand());
|
||||||
addCommand(new GetUserStatusCommand());
|
addCommand(new GetUserStatusCommand());
|
||||||
|
addCommand(new HttpServerCommand());
|
||||||
addCommand(new JoinGroupCommand());
|
addCommand(new JoinGroupCommand());
|
||||||
addCommand(new JsonRpcDispatcherCommand());
|
addCommand(new JsonRpcDispatcherCommand());
|
||||||
addCommand(new LinkCommand());
|
addCommand(new LinkCommand());
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
package org.asamk.signal.commands;
|
||||||
|
|
||||||
|
import net.sourceforge.argparse4j.inf.Namespace;
|
||||||
|
import net.sourceforge.argparse4j.inf.Subparser;
|
||||||
|
|
||||||
|
import org.asamk.signal.commands.exceptions.CommandException;
|
||||||
|
import org.asamk.signal.http.HttpServerHandler;
|
||||||
|
import org.asamk.signal.manager.MultiAccountManager;
|
||||||
|
import org.asamk.signal.output.OutputWriter;
|
||||||
|
|
||||||
|
public class HttpServerCommand implements MultiLocalCommand {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "http";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void attachToSubparser(final Subparser subparser) {
|
||||||
|
subparser.help("Takes commands via an http connection");
|
||||||
|
subparser.addArgument("--port")
|
||||||
|
.help("The port on which to open the HTTP service")
|
||||||
|
.type(Integer.class)
|
||||||
|
.setDefault(8080);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleCommand(
|
||||||
|
final Namespace ns,
|
||||||
|
final MultiAccountManager m,
|
||||||
|
final OutputWriter outputWriter
|
||||||
|
) throws CommandException {
|
||||||
|
|
||||||
|
final var port = ns.getInt("port");
|
||||||
|
|
||||||
|
final var handler = new HttpServerHandler();
|
||||||
|
handler.init(port, m);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,7 +10,7 @@ import java.util.Map;
|
||||||
/**
|
/**
|
||||||
* Namespace implementation, that has plural handling for list arguments and converts camel case keys to dashed strings
|
* Namespace implementation, that has plural handling for list arguments and converts camel case keys to dashed strings
|
||||||
*/
|
*/
|
||||||
final class JsonRpcNamespace extends Namespace {
|
final public class JsonRpcNamespace extends Namespace {
|
||||||
|
|
||||||
public JsonRpcNamespace(final Map<String, Object> attrs) {
|
public JsonRpcNamespace(final Map<String, Object> attrs) {
|
||||||
super(attrs);
|
super(attrs);
|
||||||
|
|
111
src/main/java/org/asamk/signal/http/HttpServerHandler.java
Normal file
111
src/main/java/org/asamk/signal/http/HttpServerHandler.java
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
package org.asamk.signal.http;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.node.ContainerNode;
|
||||||
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
|
import com.sun.net.httpserver.HttpExchange;
|
||||||
|
import com.sun.net.httpserver.HttpServer;
|
||||||
|
|
||||||
|
import org.asamk.signal.commands.Commands;
|
||||||
|
import org.asamk.signal.commands.JsonRpcNamespace;
|
||||||
|
import org.asamk.signal.commands.LocalCommand;
|
||||||
|
import org.asamk.signal.jsonrpc.JsonRpcException;
|
||||||
|
import org.asamk.signal.jsonrpc.JsonRpcRequest;
|
||||||
|
import org.asamk.signal.jsonrpc.JsonRpcResponse;
|
||||||
|
import org.asamk.signal.manager.Manager;
|
||||||
|
import org.asamk.signal.manager.MultiAccountManager;
|
||||||
|
import org.asamk.signal.output.JsonWriterImpl;
|
||||||
|
import org.asamk.signal.util.Util;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
|
public class HttpServerHandler {
|
||||||
|
|
||||||
|
private final ObjectMapper objectMapper = Util.createJsonObjectMapper();
|
||||||
|
|
||||||
|
public void init(int port, MultiAccountManager m) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
System.out.println("Starting server on port " + port);
|
||||||
|
|
||||||
|
final var server = HttpServer.create(new InetSocketAddress(port), 0);
|
||||||
|
server.setExecutor(Executors.newFixedThreadPool(10));
|
||||||
|
|
||||||
|
server.createContext("/api/v1", httpExchange -> {
|
||||||
|
try {
|
||||||
|
if (!"POST".equals(httpExchange.getRequestMethod())) {
|
||||||
|
sendResponse(405, "NOT SUPPORTED", httpExchange);
|
||||||
|
}
|
||||||
|
|
||||||
|
final var request = objectMapper.readValue(httpExchange.getRequestBody(), JsonRpcRequest.class);
|
||||||
|
final Map params = objectMapper.treeToValue(request.getParams(), Map.class);
|
||||||
|
|
||||||
|
System.out.println("Command called " + request.getMethod());
|
||||||
|
|
||||||
|
final var command = Commands.getCommand(request.getMethod());
|
||||||
|
final var manager = getManagerFromParams(request.getParams(), m);
|
||||||
|
|
||||||
|
if (command instanceof LocalCommand) {
|
||||||
|
final var writer = new StringWriter();
|
||||||
|
final var ns = new JsonRpcNamespace(params);
|
||||||
|
|
||||||
|
((LocalCommand) command).handleCommand(ns, manager, new JsonWriterImpl(writer));
|
||||||
|
|
||||||
|
sendResponse(200, writer.toString(), httpExchange);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sendResponse(404, "COMMAND NOT FOUND", httpExchange);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Throwable aEx) {
|
||||||
|
aEx.printStackTrace();
|
||||||
|
sendResponse(500, "ERROR", httpExchange);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.start();
|
||||||
|
|
||||||
|
// TODO there may be a better way to keep the main thread running.
|
||||||
|
try {
|
||||||
|
while (true) {
|
||||||
|
Thread.sleep(1000);
|
||||||
|
}
|
||||||
|
} catch (InterruptedException ex) { }
|
||||||
|
|
||||||
|
System.out.println("Server shut down");
|
||||||
|
|
||||||
|
} catch (Throwable ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendResponse(int status, String body, HttpExchange httpExchange) throws IOException {
|
||||||
|
final var byteResponse = body.getBytes(StandardCharsets.UTF_8);
|
||||||
|
httpExchange.sendResponseHeaders(status, byteResponse.length);
|
||||||
|
httpExchange.getResponseBody().write(byteResponse);
|
||||||
|
httpExchange.getResponseBody().close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Manager getManagerFromParams(final ContainerNode<?> params, MultiAccountManager c) throws JsonRpcException {
|
||||||
|
if (params != null && params.hasNonNull("account")) {
|
||||||
|
final var manager = c.getManager(params.get("account").asText());
|
||||||
|
((ObjectNode) params).remove("account");
|
||||||
|
if (manager == null) {
|
||||||
|
throw new JsonRpcException(new JsonRpcResponse.Error(JsonRpcResponse.Error.INVALID_PARAMS,
|
||||||
|
"Specified account does not exist",
|
||||||
|
null));
|
||||||
|
}
|
||||||
|
return manager;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue