add /y/shell-exec in HTTP server

This commit is contained in:
Martin Michelsen
2025-01-11 22:16:26 -08:00
parent 80dda2e1f9
commit b028532db3
15 changed files with 1385 additions and 1248 deletions
+49
View File
@@ -14,6 +14,7 @@
#include "Loggers.hh"
#include "ProxyServer.hh"
#include "Server.hh"
#include "ShellCommands.hh"
using namespace std;
@@ -1154,6 +1155,25 @@ phosg::JSON HTTPServer::generate_rare_table_json(const std::string& table_name)
}
}
void HTTPServer::require_GET(struct evhttp_request* req) {
if (evhttp_request_get_command(req) != EVHTTP_REQ_GET) {
throw HTTPServer::http_error(405, "GET method required for this endpoint");
}
}
phosg::JSON HTTPServer::require_POST(struct evhttp_request* req) {
if (evhttp_request_get_command(req) != EVHTTP_REQ_POST) {
throw HTTPServer::http_error(405, "POST method required for this endpoint");
}
const evkeyvalq* headers = evhttp_request_get_input_headers(req);
const char* content_type = evhttp_find_header(headers, "Content-Type");
if (!content_type || strcmp(content_type, "application/json")) {
throw HTTPServer::http_error(400, "POST requests must use the application/json content type");
}
struct evbuffer* in_buf = evhttp_request_get_input_buffer(req);
return phosg::JSON::parse(evbuffer_remove_str(in_buf));
}
void HTTPServer::handle_request(struct evhttp_request* req) {
shared_ptr<const phosg::JSON> ret;
uint32_t serialize_options = 0;
@@ -1177,6 +1197,7 @@ void HTTPServer::handle_request(struct evhttp_request* req) {
}
if (uri == "/") {
this->require_GET(req);
auto endpoints_json = phosg::JSON::list({
"/y/data/ep3-cards",
"/y/data/ep3-cards-trial",
@@ -1191,10 +1212,22 @@ void HTTPServer::handle_request(struct evhttp_request* req) {
"/y/rare-drops/stream",
"/y/summary",
"/y/all",
"/y/shell-exec",
});
ret = make_shared<phosg::JSON>(phosg::JSON::dict({{"endpoints", std::move(endpoints_json)}}));
} else if (uri == "/y/shell-exec") {
auto json = this->require_POST(req);
auto command = json.get_string("command");
try {
ret = make_shared<phosg::JSON>(phosg::JSON::dict(
{{"result", phosg::join(ShellCommand::dispatch_str(this->state, command), "\n")}}));
} catch (const exception& e) {
throw http_error(400, e.what());
}
} else if (uri == "/y/rare-drops/stream") {
this->require_GET(req);
auto c = this->enable_websockets(req);
if (!c) {
throw http_error(400, "this path requires a websocket connection");
@@ -1206,28 +1239,40 @@ void HTTPServer::handle_request(struct evhttp_request* req) {
}
} else if (uri == "/y/data/ep3-cards") {
this->require_GET(req);
ret = make_shared<phosg::JSON>(this->generate_ep3_cards_json(false));
} else if (uri == "/y/data/ep3-cards-trial") {
this->require_GET(req);
ret = make_shared<phosg::JSON>(this->generate_ep3_cards_json(true));
} else if (uri == "/y/data/common-tables") {
this->require_GET(req);
ret = make_shared<phosg::JSON>(this->generate_common_tables_json());
} else if (uri == "/y/data/rare-tables") {
this->require_GET(req);
ret = make_shared<phosg::JSON>(this->generate_rare_tables_json());
} else if (!strncmp(uri.c_str(), "/y/data/rare-tables/", 20)) {
this->require_GET(req);
ret = make_shared<phosg::JSON>(this->generate_rare_table_json(uri.substr(20)));
} else if (uri == "/y/data/config") {
this->require_GET(req);
ret = call_on_event_thread<shared_ptr<const phosg::JSON>>(this->state->base, [this]() { return this->state->config_json; });
} else if (uri == "/y/clients") {
this->require_GET(req);
ret = make_shared<phosg::JSON>(this->generate_game_server_clients_json());
} else if (uri == "/y/proxy-clients") {
this->require_GET(req);
ret = make_shared<phosg::JSON>(this->generate_proxy_server_clients_json());
} else if (uri == "/y/lobbies") {
this->require_GET(req);
ret = make_shared<phosg::JSON>(this->generate_lobbies_json());
} else if (uri == "/y/server") {
this->require_GET(req);
ret = make_shared<phosg::JSON>(this->generate_server_info_json());
} else if (uri == "/y/summary") {
this->require_GET(req);
ret = make_shared<phosg::JSON>(this->generate_summary_json());
} else if (uri == "/y/all") {
this->require_GET(req);
ret = make_shared<phosg::JSON>(this->generate_all_json());
} else {
@@ -1248,6 +1293,10 @@ void HTTPServer::handle_request(struct evhttp_request* req) {
return;
}
if (!ret) {
throw logic_error("ret was not set after HTTP handler completed");
}
uint64_t handler_end = phosg::now();
unique_ptr<struct evbuffer, void (*)(struct evbuffer*)> out_buffer(evbuffer_new(), evbuffer_free);
string* serialized = new string(ret->serialize(phosg::JSON::SerializeOption::ESCAPE_CONTROLS_ONLY | serialize_options));