diff --git a/include/dsn/tool-api/http_server.h b/include/dsn/tool-api/http_server.h index 4a1a5fc97d..6e8e231772 100644 --- a/include/dsn/tool-api/http_server.h +++ b/include/dsn/tool-api/http_server.h @@ -60,30 +60,45 @@ class http_service virtual std::string path() const = 0; - void register_handler(std::string path, http_callback cb) + void register_handler(std::string path, http_callback cb, std::string help) { - _cb_map.emplace(std::move(path), std::move(cb)); + _cb_map.emplace(std::move(path), std::make_pair(std::move(cb), std::move(help))); } void call(const http_request &req, http_response &resp) { auto it = _cb_map.find(req.service_method.second); if (it != _cb_map.end()) { - it->second(req, resp); + it->second.first(req, resp); } else { resp.status_code = http_status_code::not_found; resp.body = std::string("method not found for \"") + req.service_method.second + "\""; } } + struct method_help_entry + { + std::string name; + std::string help; + }; + std::vector get_help() const + { + std::vector ret; + ret.reserve(_cb_map.size()); + for (const auto &method : _cb_map) { + ret.push_back({method.first, method.second.second}); + } + return ret; + } + private: - std::map _cb_map; + std::map> _cb_map; }; class http_server : public serverlet { public: - http_server(); + explicit http_server(bool start = true); ~http_server() override = default; @@ -91,6 +106,23 @@ class http_server : public serverlet void serve(message_ex *msg); + struct service_method_help_entry + { + std::string name; + std::string method; + std::string help; + }; + std::vector get_help() const + { + std::vector ret; + for (const auto &service : _service_map) { + for (const auto &method : service.second->get_help()) { + ret.push_back({service.first, method.name, method.help}); + } + } + return ret; + } + private: std::map> _service_map; }; diff --git a/src/dist/http/http_server.cpp b/src/dist/http/http_server.cpp index 15addaacdc..e03c3a9bb9 100644 --- a/src/dist/http/http_server.cpp +++ b/src/dist/http/http_server.cpp @@ -34,14 +34,18 @@ namespace dsn { } } -http_server::http_server() : serverlet("http_server") +http_server::http_server(bool start /*default=true*/) : serverlet("http_server") { + if (!start) { + return; + } + register_rpc_handler(RPC_HTTP_SERVICE, "http_service", &http_server::serve); tools::register_message_header_parser(NET_HDR_HTTP, {"GET ", "POST"}); // add builtin services - add_service(new root_http_service()); + add_service(new root_http_service(this)); #ifdef DSN_ENABLE_GPERF add_service(new pprof_http_service()); diff --git a/src/dist/http/perf_counter_http_service.h b/src/dist/http/perf_counter_http_service.h index d00b10f19f..c8107f6b92 100644 --- a/src/dist/http/perf_counter_http_service.h +++ b/src/dist/http/perf_counter_http_service.h @@ -13,12 +13,12 @@ class perf_counter_http_service : public http_service public: perf_counter_http_service() { - // GET ip:port/perfCounter?name={perf_counter_name} register_handler("", std::bind(&perf_counter_http_service::get_perf_counter_handler, this, std::placeholders::_1, - std::placeholders::_2)); + std::placeholders::_2), + "ip:port/perfCounter?name={perf_counter_name}"); } std::string path() const override { return "perfCounter"; } diff --git a/src/dist/http/pprof_http_service.h b/src/dist/http/pprof_http_service.h index 0704c9ac1a..2cf59db960 100644 --- a/src/dist/http/pprof_http_service.h +++ b/src/dist/http/pprof_http_service.h @@ -15,40 +15,36 @@ class pprof_http_service : public http_service public: pprof_http_service() { - // ip:port/pprof/heap register_handler("heap", std::bind(&pprof_http_service::heap_handler, this, std::placeholders::_1, - std::placeholders::_2)); - - // ip:port/pprof/symbol + std::placeholders::_2), + "ip:port/pprof/heap"); register_handler("symbol", std::bind(&pprof_http_service::symbol_handler, this, std::placeholders::_1, - std::placeholders::_2)); - - // ip:port/pprof/cmdline + std::placeholders::_2), + "ip:port/pprof/symbol"); register_handler("cmdline", std::bind(&pprof_http_service::cmdline_handler, this, std::placeholders::_1, - std::placeholders::_2)); - - // ip:port/pprof/growth + std::placeholders::_2), + "ip:port/pprof/cmdline"); register_handler("growth", std::bind(&pprof_http_service::growth_handler, this, std::placeholders::_1, - std::placeholders::_2)); - - // ip:port/pprof/profile + std::placeholders::_2), + "ip:port/pprof/growth"); register_handler("profile", std::bind(&pprof_http_service::profile_handler, this, std::placeholders::_1, - std::placeholders::_2)); + std::placeholders::_2), + "ip:port/pprof/profile"); } std::string path() const override { return "pprof"; } diff --git a/src/dist/http/root_http_service.h b/src/dist/http/root_http_service.h index 66c4869291..9b15361616 100644 --- a/src/dist/http/root_http_service.h +++ b/src/dist/http/root_http_service.h @@ -3,6 +3,7 @@ // can be found in the LICENSE file in the root directory of this source tree. #include +#include #include namespace dsn { @@ -10,23 +11,36 @@ namespace dsn { class root_http_service : public http_service { public: - root_http_service() + explicit root_http_service(http_server *server) : _server(server) { // url: ip:port/ register_handler("", std::bind(&root_http_service::default_handler, this, std::placeholders::_1, - std::placeholders::_2)); + std::placeholders::_2), + "ip:port/"); } std::string path() const override { return ""; } void default_handler(const http_request &req, http_response &resp) { - resp.body = "hello world"; + utils::table_printer tp; + std::ostringstream oss; + auto help_entries = _server->get_help(); + for (const auto &ent : help_entries) { + tp.add_row_name_and_data(std::string("/") + ent.name + (ent.method.empty() ? "" : "/") + + ent.method, + ent.help); + } + tp.output(oss, utils::table_printer::output_format::kJsonCompact); + resp.body = oss.str(); resp.status_code = http_status_code::ok; } + +private: + http_server *_server; }; } // namespace dsn diff --git a/src/dist/http/server_info_http_services.h b/src/dist/http/server_info_http_services.h index 8d94f014e8..fc5969d2ba 100644 --- a/src/dist/http/server_info_http_services.h +++ b/src/dist/http/server_info_http_services.h @@ -13,12 +13,12 @@ class version_http_service : public http_service public: version_http_service() { - // GET ip:port/version register_handler("", std::bind(&version_http_service::get_version_handler, this, std::placeholders::_1, - std::placeholders::_2)); + std::placeholders::_2), + "ip:port/version"); } std::string path() const override { return "version"; } @@ -39,12 +39,12 @@ class recent_start_time_http_service : public http_service public: recent_start_time_http_service() { - // GET ip:port/recentStartTime register_handler("", std::bind(&recent_start_time_http_service::get_recent_start_time_handler, this, std::placeholders::_1, - std::placeholders::_2)); + std::placeholders::_2), + "ip:port/recentStartTime"); } std::string path() const override { return "recentStartTime"; } diff --git a/src/dist/http/test/http_server_test.cpp b/src/dist/http/test/http_server_test.cpp index 85d2bd1d29..7dd024a1c7 100644 --- a/src/dist/http/test/http_server_test.cpp +++ b/src/dist/http/test/http_server_test.cpp @@ -6,6 +6,8 @@ #include #include "dist/http/http_message_parser.h" +#include "dist/http/root_http_service.h" +#include "dist/http/server_info_http_services.h" namespace dsn { @@ -42,6 +44,26 @@ TEST(http_server, parse_url) } } +TEST(root_http_service_test, get_help) +{ + http_server server(false); + auto root = new root_http_service(&server); + server.add_service(root); + ASSERT_EQ(server.get_help().size(), 1); + + http_request req; + http_response resp; + root->default_handler(req, resp); + ASSERT_EQ(resp.status_code, http_status_code::ok); + ASSERT_EQ(resp.body, "{\"/\":\"ip:port/\"}\n"); + + auto ver = new version_http_service(); + server.add_service(ver); + ASSERT_EQ(server.get_help().size(), 2); + root->default_handler(req, resp); + ASSERT_EQ(resp.body, "{\"/\":\"ip:port/\",\"/version\":\"ip:port/version\"}\n"); +} + class http_message_parser_test : public testing::Test { public: diff --git a/src/dist/replication/meta_server/meta_http_service.h b/src/dist/replication/meta_server/meta_http_service.h index e6130c2da7..ae7a96b846 100644 --- a/src/dist/replication/meta_server/meta_http_service.h +++ b/src/dist/replication/meta_server/meta_http_service.h @@ -17,42 +17,42 @@ class meta_http_service : public http_service public: explicit meta_http_service(meta_service *s) : _service(s) { - // GET ip:port/meta/app?app_name=temp register_handler("app", std::bind(&meta_http_service::get_app_handler, this, std::placeholders::_1, - std::placeholders::_2)); - // GET ip:port/meta/apps + std::placeholders::_2), + "ip:port/meta/app?app_name=temp"); register_handler("apps", std::bind(&meta_http_service::list_app_handler, this, std::placeholders::_1, - std::placeholders::_2)); - // GET ip:port/meta/nodes + std::placeholders::_2), + "ip:port/meta/apps"); register_handler("nodes", std::bind(&meta_http_service::list_node_handler, this, std::placeholders::_1, - std::placeholders::_2)); - // GET ip:port/meta/cluster + std::placeholders::_2), + "ip:port/meta/nodes"); register_handler("cluster", std::bind(&meta_http_service::get_cluster_info_handler, this, std::placeholders::_1, - std::placeholders::_2)); - // GET ip:port/meta/app_envs?name=temp + std::placeholders::_2), + "ip:port/meta/cluster"); register_handler("app_envs", std::bind(&meta_http_service::get_app_envs_handler, this, std::placeholders::_1, - std::placeholders::_2)); - // GET ip:port/meta/backup_policy + std::placeholders::_2), + "ip:port/meta/app_envs?name=temp"); register_handler("backup_policy", std::bind(&meta_http_service::query_backup_policy_handler, this, std::placeholders::_1, - std::placeholders::_2)); + std::placeholders::_2), + "ip:port/meta/backup_policy"); } std::string path() const override { return "meta"; }