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() ->
[{group, http},
{group, https},
{group, ipv6http},
{group, ipv6https}].
[ {group, http}
, {group, https}
, {group, ipv6http}
, {group, ipv6https}
, {group, all}
].
groups() ->
Cases = emqx_ct:all(?MODULE),
[{http, [sequence], Cases},
{https, [sequence], Cases},
{ipv6http, [sequence], Cases},
{ipv6https, [sequence], Cases}].
Cases = [test_full_flow],
[ {http, [sequence], Cases}
, {https, [sequence], Cases}
, {ipv6http, [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) ->
application:ensure_all_started(emqx_management),
set_special_cfgs(),
case Name of
http ->
emqx_ct_helpers:start_apps(apps(), fun set_special_configs_http/1);
https ->
emqx_ct_helpers:start_apps(apps(), fun set_special_configs_https/1);
ipv6http ->
emqx_ct_helpers:start_apps(apps(), fun set_special_configs_ipv6_http/1);
ipv6https ->
emqx_ct_helpers:start_apps(apps(), fun set_special_configs_ipv6_https/1)
end,
Config.
BasePort =
case Name of
all -> 8801;
http -> 8811;
https -> 8821;
ipv6http -> 8831;
ipv6https -> 8841
end,
CF = case Name of
all -> fun set_special_configs_http/1;
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) ->
emqx_ct_helpers:stop_apps(apps()),
Config.
set_special_configs_http(_) ->
application:set_env(emqx_web_hook, url, "http://127.0.0.1:9999").
set_special_configs_http(Port) ->
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/"),
SslOpts = [{keyfile, Path ++ "/client-key.pem"},
{certfile, Path ++ "/client-cert.pem"},
{cacertfile, Path ++ "/ca.pem"}],
application:set_env(emqx_web_hook, ssl, true),
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").
application:set_env(emqx_web_hook, ssloptions, SslOpts).
set_special_cfgs() ->
AllRules = [{"message.acked", "{\"action\": \"on_message_acked\"}"},
@ -95,34 +114,63 @@ set_special_cfgs() ->
{"client.connack", "{\"action\": \"on_client_connack\"}"},
{"client.connect", "{\"action\": \"on_client_connect\"}"}],
application:set_env(emqx_web_hook, rules, AllRules).
%%--------------------------------------------------------------------
%% Test cases
%%--------------------------------------------------------------------
t_valid(Config) ->
{ok, ServerPid} = http_server:start_link(),
test_full_flow(Config) ->
[_|_] = 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"}]),
{ok, C} = emqtt:start_link([ {clientid, <<"simpleClient">>}
, {proto_ver, v5}
, {keepalive, 60}
]),
try
{ok, _} = emqtt:connect(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()]
try
do_test_full_flow(C)
after
http_server:stop(ServerPid)
end,
Config.
Ref = erlang:monitor(process, ServerPid),
http_server:stop(ServerPid),
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(_) ->
{ok, Rules} = application:get_env(emqx_web_hook, rules),

View File

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