145 lines
5.2 KiB
Erlang
145 lines
5.2 KiB
Erlang
%%--------------------------------------------------------------------
|
|
%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved.
|
|
%%
|
|
%% 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(emqx_mgmt_http).
|
|
|
|
-export([ start_listeners/0
|
|
, handle_request/2
|
|
, stop_listeners/0
|
|
, start_listener/1
|
|
, stop_listener/1
|
|
]).
|
|
|
|
-export([init/2]).
|
|
|
|
-export([ filter/1
|
|
, authorize_appid/1
|
|
]).
|
|
|
|
-include_lib("emqx/include/emqx.hrl").
|
|
|
|
-define(APP, emqx_management).
|
|
-define(EXCEPT_PLUGIN, [emqx_dashboard]).
|
|
-ifdef(TEST).
|
|
-define(EXCEPT, []).
|
|
-else.
|
|
-define(EXCEPT, [add_app, del_app, list_apps, lookup_app, update_app]).
|
|
-endif.
|
|
|
|
%%--------------------------------------------------------------------
|
|
%% Start/Stop Listeners
|
|
%%--------------------------------------------------------------------
|
|
|
|
start_listeners() ->
|
|
lists:foreach(fun start_listener/1, listeners()).
|
|
|
|
stop_listeners() ->
|
|
lists:foreach(fun stop_listener/1, listeners()).
|
|
|
|
start_listener({Proto, Port, Options}) when Proto == http ->
|
|
Dispatch = [{"/status", emqx_mgmt_http, []},
|
|
{"/api/v4/[...]", minirest, http_handlers()}],
|
|
minirest:start_http(listener_name(Proto), ranch_opts(Port, Options), Dispatch);
|
|
|
|
start_listener({Proto, Port, Options}) when Proto == https ->
|
|
Dispatch = [{"/status", emqx_mgmt_http, []},
|
|
{"/api/v4/[...]", minirest, http_handlers()}],
|
|
minirest:start_https(listener_name(Proto), ranch_opts(Port, Options), Dispatch).
|
|
|
|
ranch_opts(Port, Options0) ->
|
|
NumAcceptors = proplists:get_value(num_acceptors, Options0, 4),
|
|
MaxConnections = proplists:get_value(max_connections, Options0, 512),
|
|
Options = lists:foldl(fun({K, _V}, Acc) when K =:= max_connections orelse K =:= num_acceptors ->
|
|
Acc;
|
|
({inet6, true}, Acc) -> [inet6 | Acc];
|
|
({inet6, false}, Acc) -> Acc;
|
|
({ipv6_v6only, true}, Acc) -> [{ipv6_v6only, true} | Acc];
|
|
({ipv6_v6only, false}, Acc) -> Acc;
|
|
({K, V}, Acc)->
|
|
[{K, V} | Acc]
|
|
end, [], Options0),
|
|
|
|
Res = #{num_acceptors => NumAcceptors,
|
|
max_connections => MaxConnections,
|
|
socket_opts => [{port, Port} | Options]},
|
|
Res.
|
|
|
|
stop_listener({Proto, Port, _}) ->
|
|
io:format("Stop http:management listener on ~s successfully.~n",[format(Port)]),
|
|
minirest:stop_http(listener_name(Proto)).
|
|
|
|
listeners() ->
|
|
application:get_env(?APP, listeners, []).
|
|
|
|
listener_name(Proto) ->
|
|
list_to_atom(atom_to_list(Proto) ++ ":management").
|
|
|
|
http_handlers() ->
|
|
Plugins = lists:map(fun(Plugin) -> Plugin#plugin.name end, emqx_plugins:list()),
|
|
[{"/api/v4", minirest:handler(#{apps => Plugins ++ [emqx_modules] -- ?EXCEPT_PLUGIN,
|
|
except => ?EXCEPT,
|
|
filter => fun ?MODULE:filter/1}),
|
|
[{authorization, fun ?MODULE:authorize_appid/1}]}].
|
|
|
|
%%--------------------------------------------------------------------
|
|
%% Handle 'status' request
|
|
%%--------------------------------------------------------------------
|
|
init(Req, Opts) ->
|
|
Req1 = handle_request(cowboy_req:path(Req), Req),
|
|
{ok, Req1, Opts}.
|
|
|
|
handle_request(Path, Req) ->
|
|
handle_request(cowboy_req:method(Req), Path, Req).
|
|
|
|
handle_request(<<"GET">>, <<"/status">>, Req) ->
|
|
{InternalStatus, _ProvidedStatus} = init:get_status(),
|
|
AppStatus = case lists:keysearch(emqx, 1, application:which_applications()) of
|
|
false -> not_running;
|
|
{value, _Val} -> running
|
|
end,
|
|
Status = io_lib:format("Node ~s is ~s~nemqx is ~s",
|
|
[node(), InternalStatus, AppStatus]),
|
|
cowboy_req:reply(200, #{<<"content-type">> => <<"text/plain">>}, Status, Req);
|
|
|
|
handle_request(_Method, _Path, Req) ->
|
|
cowboy_req:reply(400, #{<<"content-type">> => <<"text/plain">>}, <<"Not found.">>, Req).
|
|
|
|
authorize_appid(Req) ->
|
|
case cowboy_req:parse_header(<<"authorization">>, Req) of
|
|
{basic, AppId, AppSecret} -> emqx_mgmt_auth:is_authorized(AppId, AppSecret);
|
|
_ -> false
|
|
end.
|
|
|
|
-ifdef(EMQX_ENTERPRISE).
|
|
filter(_) ->
|
|
true.
|
|
-else.
|
|
filter(#{app := emqx_modules}) -> true;
|
|
filter(#{app := App}) ->
|
|
case emqx_plugins:find_plugin(App) of
|
|
false -> false;
|
|
Plugin -> Plugin#plugin.active
|
|
end.
|
|
-endif.
|
|
|
|
|
|
format(Port) when is_integer(Port) ->
|
|
io_lib:format("0.0.0.0:~w", [Port]);
|
|
format({Addr, Port}) when is_list(Addr) ->
|
|
io_lib:format("~s:~w", [Addr, Port]);
|
|
format({Addr, Port}) when is_tuple(Addr) ->
|
|
io_lib:format("~s:~w", [inet:ntoa(Addr), Port]).
|