diff options
author | Joan Touzet <joant@atypical.net> | 2019-10-11 16:40:08 +0100 |
---|---|---|
committer | Robert Newson <rnewson@apache.org> | 2019-12-02 17:51:44 +0000 |
commit | 971ae58a04db50116bdd366a0684aa6c92f0c5f9 (patch) | |
tree | e189b3ba002b66ebcfb9c27fca10c92876f3d2c9 | |
parent | 2d734f38096bd72cd4c6bed3059453799d1a521f (diff) | |
download | couchdb-971ae58a04db50116bdd366a0684aa6c92f0c5f9.tar.gz |
Move _node handler to new module
-rw-r--r-- | src/chttpd/src/chttpd_httpd_handlers.erl | 2 | ||||
-rw-r--r-- | src/chttpd/src/chttpd_misc.erl | 127 | ||||
-rw-r--r-- | src/chttpd/src/chttpd_node.erl | 150 | ||||
-rw-r--r-- | src/couch/test/eunit/chttpd_endpoints_tests.erl | 2 |
4 files changed, 152 insertions, 129 deletions
diff --git a/src/chttpd/src/chttpd_httpd_handlers.erl b/src/chttpd/src/chttpd_httpd_handlers.erl index 000f29b2f..5e86ea87d 100644 --- a/src/chttpd/src/chttpd_httpd_handlers.erl +++ b/src/chttpd/src/chttpd_httpd_handlers.erl @@ -21,7 +21,7 @@ url_handler(<<"_all_dbs">>) -> fun chttpd_misc:handle_all_dbs_req/1; url_handler(<<"_dbs_info">>) -> fun chttpd_misc:handle_dbs_info_req/1; url_handler(<<"_active_tasks">>) -> fun chttpd_misc:handle_task_status_req/1; url_handler(<<"_scheduler">>) -> fun couch_replicator_httpd:handle_scheduler_req/1; -url_handler(<<"_node">>) -> fun chttpd_misc:handle_node_req/1; +url_handler(<<"_node">>) -> fun chttpd_node:handle_node_req/1; url_handler(<<"_reload_query_servers">>) -> fun chttpd_misc:handle_reload_query_servers_req/1; url_handler(<<"_replicate">>) -> fun chttpd_misc:handle_replicate_req/1; url_handler(<<"_uuids">>) -> fun chttpd_misc:handle_uuids_req/1; diff --git a/src/chttpd/src/chttpd_misc.erl b/src/chttpd/src/chttpd_misc.erl index 17122bf85..dd247128b 100644 --- a/src/chttpd/src/chttpd_misc.erl +++ b/src/chttpd/src/chttpd_misc.erl @@ -15,7 +15,6 @@ -export([ handle_all_dbs_req/1, handle_dbs_info_req/1, - handle_node_req/1, handle_favicon_req/1, handle_favicon_req/2, handle_replicate_req/1, @@ -275,132 +274,6 @@ handle_uuids_req(Req) -> couch_httpd_misc_handlers:handle_uuids_req(Req). -% Node-specific request handler (_config and _stats) -% Support _local meaning this node -handle_node_req(#httpd{path_parts=[_, <<"_local">>]}=Req) -> - send_json(Req, 200, {[{name, node()}]}); -handle_node_req(#httpd{path_parts=[A, <<"_local">>|Rest]}=Req) -> - handle_node_req(Req#httpd{path_parts=[A, node()] ++ Rest}); -% GET /_node/$node/_config -handle_node_req(#httpd{method='GET', path_parts=[_, Node, <<"_config">>]}=Req) -> - Grouped = lists:foldl(fun({{Section, Key}, Value}, Acc) -> - case dict:is_key(Section, Acc) of - true -> - dict:append(Section, {list_to_binary(Key), list_to_binary(Value)}, Acc); - false -> - dict:store(Section, [{list_to_binary(Key), list_to_binary(Value)}], Acc) - end - end, dict:new(), call_node(Node, config, all, [])), - KVs = dict:fold(fun(Section, Values, Acc) -> - [{list_to_binary(Section), {Values}} | Acc] - end, [], Grouped), - send_json(Req, 200, {KVs}); -handle_node_req(#httpd{path_parts=[_, _Node, <<"_config">>]}=Req) -> - send_method_not_allowed(Req, "GET"); -% GET /_node/$node/_config/Section -handle_node_req(#httpd{method='GET', path_parts=[_, Node, <<"_config">>, Section]}=Req) -> - KVs = [{list_to_binary(Key), list_to_binary(Value)} - || {Key, Value} <- call_node(Node, config, get, [Section])], - send_json(Req, 200, {KVs}); -handle_node_req(#httpd{path_parts=[_, _Node, <<"_config">>, _Section]}=Req) -> - send_method_not_allowed(Req, "GET"); -% PUT /_node/$node/_config/Section/Key -% "value" -handle_node_req(#httpd{method='PUT', path_parts=[_, Node, <<"_config">>, Section, Key]}=Req) -> - couch_util:check_config_blacklist(Section), - Value = couch_util:trim(chttpd:json_body(Req)), - Persist = chttpd:header_value(Req, "X-Couch-Persist") /= "false", - OldValue = call_node(Node, config, get, [Section, Key, ""]), - case call_node(Node, config, set, [Section, Key, ?b2l(Value), Persist]) of - ok -> - send_json(Req, 200, list_to_binary(OldValue)); - {error, Reason} -> - chttpd:send_error(Req, {bad_request, Reason}) - end; -% GET /_node/$node/_config/Section/Key -handle_node_req(#httpd{method='GET', path_parts=[_, Node, <<"_config">>, Section, Key]}=Req) -> - case call_node(Node, config, get, [Section, Key, undefined]) of - undefined -> - throw({not_found, unknown_config_value}); - Value -> - send_json(Req, 200, list_to_binary(Value)) - end; -% DELETE /_node/$node/_config/Section/Key -handle_node_req(#httpd{method='DELETE',path_parts=[_, Node, <<"_config">>, Section, Key]}=Req) -> - couch_util:check_config_blacklist(Section), - Persist = chttpd:header_value(Req, "X-Couch-Persist") /= "false", - case call_node(Node, config, get, [Section, Key, undefined]) of - undefined -> - throw({not_found, unknown_config_value}); - OldValue -> - case call_node(Node, config, delete, [Section, Key, Persist]) of - ok -> - send_json(Req, 200, list_to_binary(OldValue)); - {error, Reason} -> - chttpd:send_error(Req, {bad_request, Reason}) - end - end; -handle_node_req(#httpd{path_parts=[_, _Node, <<"_config">>, _Section, _Key]}=Req) -> - send_method_not_allowed(Req, "GET,PUT,DELETE"); -handle_node_req(#httpd{path_parts=[_, _Node, <<"_config">>, _Section, _Key | _]}=Req) -> - chttpd:send_error(Req, not_found); -% GET /_node/$node/_stats -handle_node_req(#httpd{method='GET', path_parts=[_, Node, <<"_stats">> | Path]}=Req) -> - flush(Node, Req), - Stats0 = call_node(Node, couch_stats, fetch, []), - Stats = couch_stats_httpd:transform_stats(Stats0), - Nested = couch_stats_httpd:nest(Stats), - EJSON0 = couch_stats_httpd:to_ejson(Nested), - EJSON1 = couch_stats_httpd:extract_path(Path, EJSON0), - chttpd:send_json(Req, EJSON1); -handle_node_req(#httpd{path_parts=[_, _Node, <<"_stats">>]}=Req) -> - send_method_not_allowed(Req, "GET"); -% GET /_node/$node/_system -handle_node_req(#httpd{method='GET', path_parts=[_, Node, <<"_system">>]}=Req) -> - Stats = call_node(Node, chttpd_misc, get_stats, []), - EJSON = couch_stats_httpd:to_ejson(Stats), - send_json(Req, EJSON); -handle_node_req(#httpd{path_parts=[_, _Node, <<"_system">>]}=Req) -> - send_method_not_allowed(Req, "GET"); -% POST /_node/$node/_restart -handle_node_req(#httpd{method='POST', path_parts=[_, Node, <<"_restart">>]}=Req) -> - call_node(Node, init, restart, []), - send_json(Req, 200, {[{ok, true}]}); -handle_node_req(#httpd{path_parts=[_, _Node, <<"_restart">>]}=Req) -> - send_method_not_allowed(Req, "POST"); -handle_node_req(#httpd{path_parts=[_]}=Req) -> - chttpd:send_error(Req, {bad_request, <<"Incomplete path to _node request">>}); -handle_node_req(#httpd{path_parts=[_, _Node]}=Req) -> - chttpd:send_error(Req, {bad_request, <<"Incomplete path to _node request">>}); -handle_node_req(Req) -> - chttpd:send_error(Req, not_found). - - -call_node(Node0, Mod, Fun, Args) when is_binary(Node0) -> - Node1 = try - list_to_existing_atom(?b2l(Node0)) - catch - error:badarg -> - throw({not_found, <<"no such node: ", Node0/binary>>}) - end, - call_node(Node1, Mod, Fun, Args); -call_node(Node, Mod, Fun, Args) when is_atom(Node) -> - case rpc:call(Node, Mod, Fun, Args) of - {badrpc, nodedown} -> - Reason = ?l2b(io_lib:format("~s is down", [Node])), - throw({error, {nodedown, Reason}}); - Else -> - Else - end. - -flush(Node, Req) -> - case couch_util:get_value("flush", chttpd:qs(Req)) of - "true" -> - call_node(Node, couch_stats_aggregator, flush, []); - _Else -> - ok - end. - % Note: this resource is exposed on the backdoor interface, but it's in chttpd % because it's not couch trunk handle_system_req(Req) -> diff --git a/src/chttpd/src/chttpd_node.erl b/src/chttpd/src/chttpd_node.erl new file mode 100644 index 000000000..8ca7f575d --- /dev/null +++ b/src/chttpd/src/chttpd_node.erl @@ -0,0 +1,150 @@ +% Licensed under the Apache License, Version 2.0 (the "License"); you may not +% use this file except in compliance with the License. You may obtain a copy of +% the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +% License for the specific language governing permissions and limitations under +% the License. + +-module(chttpd_node). +-compile(tuple_calls). + +-export([ + handle_node_req/1 +]). + +-include_lib("couch/include/couch_db.hrl"). + +-import(chttpd, + [send_json/2,send_json/3,send_method_not_allowed/2, + send_chunk/2,start_chunked_response/3]). + +% Node-specific request handler (_config and _stats) +% Support _local meaning this node +handle_node_req(#httpd{path_parts=[_, <<"_local">>]}=Req) -> + send_json(Req, 200, {[{name, node()}]}); +handle_node_req(#httpd{path_parts=[A, <<"_local">>|Rest]}=Req) -> + handle_node_req(Req#httpd{path_parts=[A, node()] ++ Rest}); +% GET /_node/$node/_config +handle_node_req(#httpd{method='GET', path_parts=[_, Node, <<"_config">>]}=Req) -> + Grouped = lists:foldl(fun({{Section, Key}, Value}, Acc) -> + case dict:is_key(Section, Acc) of + true -> + dict:append(Section, {list_to_binary(Key), list_to_binary(Value)}, Acc); + false -> + dict:store(Section, [{list_to_binary(Key), list_to_binary(Value)}], Acc) + end + end, dict:new(), call_node(Node, config, all, [])), + KVs = dict:fold(fun(Section, Values, Acc) -> + [{list_to_binary(Section), {Values}} | Acc] + end, [], Grouped), + send_json(Req, 200, {KVs}); +handle_node_req(#httpd{path_parts=[_, _Node, <<"_config">>]}=Req) -> + send_method_not_allowed(Req, "GET"); +% GET /_node/$node/_config/Section +handle_node_req(#httpd{method='GET', path_parts=[_, Node, <<"_config">>, Section]}=Req) -> + KVs = [{list_to_binary(Key), list_to_binary(Value)} + || {Key, Value} <- call_node(Node, config, get, [Section])], + send_json(Req, 200, {KVs}); +handle_node_req(#httpd{path_parts=[_, _Node, <<"_config">>, _Section]}=Req) -> + send_method_not_allowed(Req, "GET"); +% PUT /_node/$node/_config/Section/Key +% "value" +handle_node_req(#httpd{method='PUT', path_parts=[_, Node, <<"_config">>, Section, Key]}=Req) -> + couch_util:check_config_blacklist(Section), + Value = couch_util:trim(chttpd:json_body(Req)), + Persist = chttpd:header_value(Req, "X-Couch-Persist") /= "false", + OldValue = call_node(Node, config, get, [Section, Key, ""]), + case call_node(Node, config, set, [Section, Key, ?b2l(Value), Persist]) of + ok -> + send_json(Req, 200, list_to_binary(OldValue)); + {error, Reason} -> + chttpd:send_error(Req, {bad_request, Reason}) + end; +% GET /_node/$node/_config/Section/Key +handle_node_req(#httpd{method='GET', path_parts=[_, Node, <<"_config">>, Section, Key]}=Req) -> + case call_node(Node, config, get, [Section, Key, undefined]) of + undefined -> + throw({not_found, unknown_config_value}); + Value -> + send_json(Req, 200, list_to_binary(Value)) + end; +% DELETE /_node/$node/_config/Section/Key +handle_node_req(#httpd{method='DELETE',path_parts=[_, Node, <<"_config">>, Section, Key]}=Req) -> + couch_util:check_config_blacklist(Section), + Persist = chttpd:header_value(Req, "X-Couch-Persist") /= "false", + case call_node(Node, config, get, [Section, Key, undefined]) of + undefined -> + throw({not_found, unknown_config_value}); + OldValue -> + case call_node(Node, config, delete, [Section, Key, Persist]) of + ok -> + send_json(Req, 200, list_to_binary(OldValue)); + {error, Reason} -> + chttpd:send_error(Req, {bad_request, Reason}) + end + end; +handle_node_req(#httpd{path_parts=[_, _Node, <<"_config">>, _Section, _Key]}=Req) -> + send_method_not_allowed(Req, "GET,PUT,DELETE"); +handle_node_req(#httpd{path_parts=[_, _Node, <<"_config">>, _Section, _Key | _]}=Req) -> + chttpd:send_error(Req, not_found); +% GET /_node/$node/_stats +handle_node_req(#httpd{method='GET', path_parts=[_, Node, <<"_stats">> | Path]}=Req) -> + flush(Node, Req), + Stats0 = call_node(Node, couch_stats, fetch, []), + Stats = couch_stats_httpd:transform_stats(Stats0), + Nested = couch_stats_httpd:nest(Stats), + EJSON0 = couch_stats_httpd:to_ejson(Nested), + EJSON1 = couch_stats_httpd:extract_path(Path, EJSON0), + chttpd:send_json(Req, EJSON1); +handle_node_req(#httpd{path_parts=[_, _Node, <<"_stats">>]}=Req) -> + send_method_not_allowed(Req, "GET"); +% GET /_node/$node/_system +handle_node_req(#httpd{method='GET', path_parts=[_, Node, <<"_system">>]}=Req) -> + Stats = call_node(Node, chttpd_misc, get_stats, []), + EJSON = couch_stats_httpd:to_ejson(Stats), + send_json(Req, EJSON); +handle_node_req(#httpd{path_parts=[_, _Node, <<"_system">>]}=Req) -> + send_method_not_allowed(Req, "GET"); +% POST /_node/$node/_restart +handle_node_req(#httpd{method='POST', path_parts=[_, Node, <<"_restart">>]}=Req) -> + call_node(Node, init, restart, []), + send_json(Req, 200, {[{ok, true}]}); +handle_node_req(#httpd{path_parts=[_, _Node, <<"_restart">>]}=Req) -> + send_method_not_allowed(Req, "POST"); +handle_node_req(#httpd{path_parts=[_]}=Req) -> + chttpd:send_error(Req, {bad_request, <<"Incomplete path to _node request">>}); +handle_node_req(#httpd{path_parts=[_, _Node]}=Req) -> + chttpd:send_error(Req, {bad_request, <<"Incomplete path to _node request">>}); +handle_node_req(Req) -> + chttpd:send_error(Req, not_found). + + +call_node(Node0, Mod, Fun, Args) when is_binary(Node0) -> + Node1 = try + list_to_existing_atom(?b2l(Node0)) + catch + error:badarg -> + throw({not_found, <<"no such node: ", Node0/binary>>}) + end, + call_node(Node1, Mod, Fun, Args); +call_node(Node, Mod, Fun, Args) when is_atom(Node) -> + case rpc:call(Node, Mod, Fun, Args) of + {badrpc, nodedown} -> + Reason = ?l2b(io_lib:format("~s is down", [Node])), + throw({error, {nodedown, Reason}}); + Else -> + Else + end. + +flush(Node, Req) -> + case couch_util:get_value("flush", chttpd:qs(Req)) of + "true" -> + call_node(Node, couch_stats_aggregator, flush, []); + _Else -> + ok + end. diff --git a/src/couch/test/eunit/chttpd_endpoints_tests.erl b/src/couch/test/eunit/chttpd_endpoints_tests.erl index 9b7430823..6433d3d89 100644 --- a/src/couch/test/eunit/chttpd_endpoints_tests.erl +++ b/src/couch/test/eunit/chttpd_endpoints_tests.erl @@ -43,7 +43,7 @@ handlers(url_handler) -> {<<"_all_dbs">>, chttpd_misc, handle_all_dbs_req}, {<<"_dbs_info">>, chttpd_misc, handle_dbs_info_req}, {<<"_active_tasks">>, chttpd_misc, handle_task_status_req}, - {<<"_node">>, chttpd_misc, handle_node_req}, + {<<"_node">>, chttpd_node, handle_node_req}, {<<"_reload_query_servers">>, chttpd_misc, handle_reload_query_servers_req}, {<<"_replicate">>, chttpd_misc, handle_replicate_req}, {<<"_uuids">>, chttpd_misc, handle_uuids_req}, |