test(webhook): test ipv6 for real

This commit is contained in:
Zaiming Shi 2021-04-19 21:32:09 +02:00 committed by Zaiming (Stone) Shi
parent 1f258a0499
commit e59eacb891
2 changed files with 123 additions and 81 deletions

View File

@ -31,55 +31,74 @@
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
all() -> all() ->
[{group, http}, [ {group, http}
{group, https}, , {group, https}
{group, ipv6http}, , {group, ipv6http}
{group, ipv6https}]. , {group, ipv6https}
, {group, all}
].
groups() -> groups() ->
Cases = emqx_ct:all(?MODULE), Cases = [test_full_flow],
[{http, [sequence], Cases}, [ {http, [sequence], Cases}
{https, [sequence], Cases}, , {https, [sequence], Cases}
{ipv6http, [sequence], Cases}, , {ipv6http, [sequence], Cases}
{ipv6https, [sequence], Cases}]. , {ipv6https, [sequence], Cases}
, {all, [sequence], emqx_ct:all(?MODULE)}
].
start_apps(F) -> emqx_ct_helpers:start_apps(apps(), F).
init_per_group(Name, Config) -> init_per_group(Name, Config) ->
application:ensure_all_started(emqx_management), application:ensure_all_started(emqx_management),
set_special_cfgs(), set_special_cfgs(),
case Name of BasePort =
http -> case Name of
emqx_ct_helpers:start_apps(apps(), fun set_special_configs_http/1); all -> 8801;
https -> http -> 8811;
emqx_ct_helpers:start_apps(apps(), fun set_special_configs_https/1); https -> 8821;
ipv6http -> ipv6http -> 8831;
emqx_ct_helpers:start_apps(apps(), fun set_special_configs_ipv6_http/1); ipv6https -> 8841
ipv6https -> end,
emqx_ct_helpers:start_apps(apps(), fun set_special_configs_ipv6_https/1) CF = case Name of
end, all -> fun set_special_configs_http/1;
Config. http -> fun set_special_configs_http/1;
https -> fun set_special_configs_https/1;
ipv6http -> fun set_special_configs_ipv6_http/1;
ipv6https -> fun set_special_configs_ipv6_https/1
end,
start_apps(fun(_) -> CF(BasePort) end),
Opts = case atom_to_list(Name) of
"ipv6" ++ _ -> [{ip, {0,0,0,0,0,0,0,1}}, inet6];
_ -> [inet]
end,
[{base_port, BasePort}, {transport_opts, Opts} | Config].
end_per_group(_Name, Config) -> end_per_group(_Name, Config) ->
emqx_ct_helpers:stop_apps(apps()), emqx_ct_helpers:stop_apps(apps()),
Config. Config.
set_special_configs_http(_) -> set_special_configs_http(Port) ->
application:set_env(emqx_web_hook, url, "http://127.0.0.1:9999"). application:set_env(emqx_web_hook, url, "http://127.0.0.1:" ++ integer_to_list(Port)).
set_special_configs_https(_) -> set_special_configs_https(Port) ->
set_ssl_configs(),
application:set_env(emqx_web_hook, url, "https://127.0.0.1:" ++ integer_to_list(Port+1)).
set_special_configs_ipv6_http(Port) ->
application:set_env(emqx_web_hook, url, "http://[::1]:" ++ integer_to_list(Port)).
set_special_configs_ipv6_https(Port) ->
set_ssl_configs(),
application:set_env(emqx_web_hook, url, "https://[::1]:" ++ integer_to_list(Port+1)).
set_ssl_configs() ->
Path = emqx_ct_helpers:deps_path(emqx_web_hook, "test/emqx_web_hook_SUITE_data/"), Path = emqx_ct_helpers:deps_path(emqx_web_hook, "test/emqx_web_hook_SUITE_data/"),
SslOpts = [{keyfile, Path ++ "/client-key.pem"}, SslOpts = [{keyfile, Path ++ "/client-key.pem"},
{certfile, Path ++ "/client-cert.pem"}, {certfile, Path ++ "/client-cert.pem"},
{cacertfile, Path ++ "/ca.pem"}], {cacertfile, Path ++ "/ca.pem"}],
application:set_env(emqx_web_hook, ssl, true), application:set_env(emqx_web_hook, ssl, true),
application:set_env(emqx_web_hook, ssloptions, SslOpts), application:set_env(emqx_web_hook, ssloptions, SslOpts).
application:set_env(emqx_web_hook, url, "https://127.0.0.1:8888").
set_special_configs_ipv6_http(_) ->
application:set_env(emqx_web_hook, url, "http://[::1]:9999").
set_special_configs_ipv6_https(N) ->
set_special_configs_https(N),
application:set_env(emqx_web_hook, url, "https://[::1]:8888").
set_special_cfgs() -> set_special_cfgs() ->
AllRules = [{"message.acked", "{\"action\": \"on_message_acked\"}"}, AllRules = [{"message.acked", "{\"action\": \"on_message_acked\"}"},
@ -95,34 +114,63 @@ set_special_cfgs() ->
{"client.connack", "{\"action\": \"on_client_connack\"}"}, {"client.connack", "{\"action\": \"on_client_connack\"}"},
{"client.connect", "{\"action\": \"on_client_connect\"}"}], {"client.connect", "{\"action\": \"on_client_connect\"}"}],
application:set_env(emqx_web_hook, rules, AllRules). application:set_env(emqx_web_hook, rules, AllRules).
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% Test cases %% Test cases
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
t_valid(Config) -> test_full_flow(Config) ->
{ok, ServerPid} = http_server:start_link(), [_|_] = Opts = proplists:get_value(transport_opts, Config),
BasePort = proplists:get_value(base_port, Config),
Tester = self(),
{ok, ServerPid} = http_server:start_link(Tester, BasePort, Opts),
receive {ServerPid, ready} -> ok
after 1000 -> error(timeout)
end,
application:set_env(emqx_web_hook, headers, [{"k1","K1"}, {"k2", "K2"}]), application:set_env(emqx_web_hook, headers, [{"k1","K1"}, {"k2", "K2"}]),
{ok, C} = emqtt:start_link([ {clientid, <<"simpleClient">>} {ok, C} = emqtt:start_link([ {clientid, <<"simpleClient">>}
, {proto_ver, v5} , {proto_ver, v5}
, {keepalive, 60} , {keepalive, 60}
]), ]),
try try
{ok, _} = emqtt:connect(C), do_test_full_flow(C)
emqtt:subscribe(C, <<"TopicA">>, qos2),
emqtt:publish(C, <<"TopicA">>, <<"Payload...">>, qos2),
emqtt:unsubscribe(C, <<"TopicA">>),
emqtt:disconnect(C),
timer:sleep(100),
[begin
Maps = emqx_json:decode(P, [return_maps]),
validate_hook_resp(Maps),
validate_hook_headers(Headers)
end
|| {{P, _Bool}, Headers} <- http_server:get_received_data()]
after after
http_server:stop(ServerPid) Ref = erlang:monitor(process, ServerPid),
end, http_server:stop(ServerPid),
Config. receive {'DOWN', Ref, _, _, _} -> ok
after 5000 -> error(timeout)
end
end.
do_test_full_flow(C) ->
{ok, _} = emqtt:connect(C),
{ok, _, _} = emqtt:subscribe(C, <<"TopicA">>, qos2),
{ok, _} = emqtt:publish(C, <<"TopicA">>, <<"Payload...">>, qos2),
{ok, _, _} = emqtt:unsubscribe(C, <<"TopicA">>),
emqtt:disconnect(C),
validate_params_and_headers(undefined).
validate_params_and_headers(ClientState) ->
receive
{http_server, {Params0, _Bool}, Headers} ->
Params = emqx_json:decode(Params0, [return_maps]),
validate_hook_resp(Params),
validate_hook_headers(Headers),
case maps:get(<<"action">>, Params) of
<<"session_terminated">> ->
ok;
<<"client_connect">> ->
validate_params_and_headers(connected);
_ ->
validate_params_and_headers(ClientState) %% continue looping
end
after
5000 ->
case ClientState =:= undefined of
true -> error("client_was_never_connected");
false -> error("terminate_action_is_not_received_in_time")
end
end.
t_check_hooked(_) -> t_check_hooked(_) ->
{ok, Rules} = application:get_env(emqx_web_hook, rules), {ok, Rules} = application:get_env(emqx_web_hook, rules),

View File

@ -9,30 +9,25 @@
-module(http_server). -module(http_server).
-behaviour(gen_server). -behaviour(gen_server).
-export([start_link/0]). -export([start_link/3]).
-export([get_received_data/0]).
-export([stop/1]). -export([stop/1]).
-export([code_change/3, handle_call/3, handle_cast/2, handle_info/2, init/1, init/2, terminate/2]). -export([code_change/3, handle_call/3, handle_cast/2, handle_info/2, init/1, init/2, terminate/2]).
-define(HTTP_PORT, 9999). -record(state, {parent :: pid()}).
-define(HTTPS_PORT, 8888).
-record(state, {}).
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% APIs %% APIs
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
start_link() -> start_link(Parent, BasePort, Opts) ->
stop_http(), stop_http(),
stop_https(), stop_https(),
timer:sleep(100), timer:sleep(100),
gen_server:start_link(?MODULE, [], []). gen_server:start_link(?MODULE, {Parent, BasePort, Opts}, []).
init([]) -> init({Parent, BasePort, Opts}) ->
EtsOptions = [named_table, public, set, {write_concurrency, true}, ok = start_http(Parent, [{port, BasePort} | Opts]),
{read_concurrency, true}], ok = start_https(Parent, [{port, BasePort + 1} | Opts]),
emqx_web_hook_http_test = ets:new(emqx_web_hook_http_test, EtsOptions), Parent ! {self(), ready},
ok = start_http(?HTTP_PORT), {ok, #state{parent = Parent}}.
ok = start_https(?HTTPS_PORT),
{ok, #state{}}.
handle_call(_Request, _From, State) -> handle_call(_Request, _From, State) ->
{reply, ignored, State}. {reply, ignored, State}.
@ -50,9 +45,6 @@ terminate(_Reason, _State) ->
code_change(_OldVsn, State, _Extra) -> code_change(_OldVsn, State, _Extra) ->
{ok, State}. {ok, State}.
get_received_data() ->
ets:tab2list(emqx_web_hook_http_test).
stop(Pid) -> stop(Pid) ->
ok = gen_server:stop(Pid). ok = gen_server:stop(Pid).
@ -60,37 +52,39 @@ stop(Pid) ->
%% Callbacks %% Callbacks
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
start_http(Port) -> start_http(Parent, Opts) ->
{ok, _Pid1} = cowboy:start_clear(http, [{port, Port}], #{ {ok, _Pid1} = cowboy:start_clear(http, Opts, #{
env => #{dispatch => compile_router()} env => #{dispatch => compile_router(Parent)}
}), }),
io:format(standard_error, "[TEST LOG] Start http server on 9999 successfully!~n", []). Port = proplists:get_value(port, Opts),
io:format(standard_error, "[TEST LOG] Start http server on ~p successfully!~n", [Port]).
start_https(Port) -> start_https(Parent, Opts) ->
Path = emqx_ct_helpers:deps_path(emqx_web_hook, "test/emqx_web_hook_SUITE_data/"), Path = emqx_ct_helpers:deps_path(emqx_web_hook, "test/emqx_web_hook_SUITE_data/"),
SslOpts = [{keyfile, Path ++ "/server-key.pem"}, SslOpts = [{keyfile, Path ++ "/server-key.pem"},
{cacertfile, Path ++ "/ca.pem"}, {cacertfile, Path ++ "/ca.pem"},
{certfile, Path ++ "/server-cert.pem"}], {certfile, Path ++ "/server-cert.pem"}],
{ok, _Pid2} = cowboy:start_tls(https, [{port, Port}] ++ SslOpts, {ok, _Pid2} = cowboy:start_tls(https, Opts ++ SslOpts,
#{env => #{dispatch => compile_router()}}), #{env => #{dispatch => compile_router(Parent)}}),
io:format(standard_error, "[TEST LOG] Start https server on 8888 successfully!~n", []). Port = proplists:get_value(port, Opts),
io:format(standard_error, "[TEST LOG] Start https server on ~p successfully!~n", [Port]).
stop_http() -> stop_http() ->
cowboy:stop_listener(http), cowboy:stop_listener(http),
io:format("[TEST LOG] Stopped http server on 9999"). io:format("[TEST LOG] Stopped http server").
stop_https() -> stop_https() ->
cowboy:stop_listener(https), cowboy:stop_listener(https),
io:format("[TEST LOG] Stopped https server on 8888"). io:format("[TEST LOG] Stopped https server").
compile_router() -> compile_router(Parent) ->
{ok, _} = application:ensure_all_started(cowboy), {ok, _} = application:ensure_all_started(cowboy),
cowboy_router:compile([ cowboy_router:compile([
{'_', [{"/", ?MODULE, #{}}]} {'_', [{"/", ?MODULE, #{parent => Parent}}]}
]). ]).
init(Req, State) -> init(Req, #{parent := Parent} = State) ->
Method = cowboy_req:method(Req), Method = cowboy_req:method(Req),
Headers = cowboy_req:headers(Req), Headers = cowboy_req:headers(Req),
[Params] = case Method of [Params] = case Method of
@ -99,7 +93,7 @@ init(Req, State) ->
{ok, PostVals, _} = cowboy_req:read_urlencoded_body(Req), {ok, PostVals, _} = cowboy_req:read_urlencoded_body(Req),
PostVals PostVals
end, end,
ets:insert(emqx_web_hook_http_test, {Params, Headers}), Parent ! {?MODULE, Params, Headers},
{ok, reply(Req, ok), State}. {ok, reply(Req, ok), State}.
reply(Req, ok) -> reply(Req, ok) ->