Update all the test cases
This commit is contained in:
parent
c4faeab45a
commit
4afa02ee48
|
@ -16,12 +16,21 @@
|
|||
|
||||
-module(emqx_tables).
|
||||
|
||||
-export([new/2, delete/1]).
|
||||
-export([ new/1
|
||||
, new/2
|
||||
]).
|
||||
|
||||
-export([ lookup_value/2
|
||||
, lookup_value/3
|
||||
]).
|
||||
|
||||
-export([delete/1]).
|
||||
|
||||
%% Create an ets table.
|
||||
-spec(new(atom()) -> ok).
|
||||
new(Tab) ->
|
||||
new(Tab, []).
|
||||
|
||||
%% Create a named_table ets.
|
||||
-spec(new(atom(), list()) -> ok).
|
||||
new(Tab, Opts) ->
|
||||
|
@ -32,26 +41,25 @@ new(Tab, Opts) ->
|
|||
Tab -> ok
|
||||
end.
|
||||
|
||||
-spec(delete(atom()) -> ok).
|
||||
%% KV lookup
|
||||
-spec(lookup_value(ets:tab(), term()) -> any()).
|
||||
lookup_value(Tab, Key) ->
|
||||
lookup_value(Tab, Key, undefined).
|
||||
|
||||
-spec(lookup_value(ets:tab(), term(), any()) -> any()).
|
||||
lookup_value(Tab, Key, Def) ->
|
||||
try ets:lookup_element(Tab, Key, 2)
|
||||
catch
|
||||
error:badarg -> Def
|
||||
end.
|
||||
|
||||
%% Delete the ets table.
|
||||
-spec(delete(ets:tab()) -> ok).
|
||||
delete(Tab) ->
|
||||
case ets:info(Tab, name) of
|
||||
undefined ->
|
||||
ok;
|
||||
undefined -> ok;
|
||||
Tab ->
|
||||
ets:delete(Tab),
|
||||
ok
|
||||
end.
|
||||
|
||||
%% KV lookup
|
||||
-spec(lookup_value(atom(), term()) -> any()).
|
||||
lookup_value(Tab, Key) ->
|
||||
lookup_value(Tab, Key, undefined).
|
||||
|
||||
-spec(lookup_value(atom(), term(), any()) -> any()).
|
||||
lookup_value(Tab, Key, Def) ->
|
||||
try
|
||||
ets:lookup_element(Tab, Key, 2)
|
||||
catch
|
||||
error:badarg -> Def
|
||||
end.
|
||||
|
||||
|
|
|
@ -21,9 +21,16 @@
|
|||
, now_secs/1
|
||||
, now_ms/0
|
||||
, now_ms/1
|
||||
, ts_from_ms/1
|
||||
]).
|
||||
|
||||
-compile({inline,
|
||||
[ seed/0
|
||||
, now_secs/0
|
||||
, now_secs/1
|
||||
, now_ms/0
|
||||
, now_ms/1
|
||||
]}).
|
||||
|
||||
seed() ->
|
||||
rand:seed(exsplus, erlang:timestamp()).
|
||||
|
||||
|
@ -39,6 +46,3 @@ now_ms() ->
|
|||
now_ms({MegaSecs, Secs, MicroSecs}) ->
|
||||
(MegaSecs * 1000000 + Secs) * 1000 + round(MicroSecs/1000).
|
||||
|
||||
ts_from_ms(Ms) ->
|
||||
{Ms div 1000000, Ms rem 1000000, 0}.
|
||||
|
||||
|
|
|
@ -16,8 +16,6 @@
|
|||
|
||||
-module(emqx_topic).
|
||||
|
||||
-include("emqx_mqtt.hrl").
|
||||
|
||||
%% APIs
|
||||
-export([ match/2
|
||||
, validate/1
|
||||
|
@ -66,7 +64,7 @@ wildcard(['+'|_]) ->
|
|||
wildcard([_H|T]) ->
|
||||
wildcard(T).
|
||||
|
||||
%% @doc Match Topic name with filter
|
||||
%% @doc Match Topic name with filter.
|
||||
-spec(match(Name, Filter) -> boolean() when
|
||||
Name :: topic() | words(),
|
||||
Filter :: topic() | words()).
|
||||
|
@ -74,7 +72,7 @@ match(<<$$, _/binary>>, <<$+, _/binary>>) ->
|
|||
false;
|
||||
match(<<$$, _/binary>>, <<$#, _/binary>>) ->
|
||||
false;
|
||||
match(Name, Filter) when is_binary(Name) and is_binary(Filter) ->
|
||||
match(Name, Filter) when is_binary(Name), is_binary(Filter) ->
|
||||
match(words(Name), words(Filter));
|
||||
match([], []) ->
|
||||
true;
|
||||
|
@ -101,13 +99,15 @@ validate({Type, Topic}) when Type =:= name; Type =:= filter ->
|
|||
-spec(validate(name | filter, topic()) -> true).
|
||||
validate(_, <<>>) ->
|
||||
error(empty_topic);
|
||||
validate(_, Topic) when is_binary(Topic) and (size(Topic) > ?MAX_TOPIC_LEN) ->
|
||||
validate(_, Topic) when is_binary(Topic) andalso (size(Topic) > ?MAX_TOPIC_LEN) ->
|
||||
error(topic_too_long);
|
||||
validate(filter, Topic) when is_binary(Topic) ->
|
||||
validate2(words(Topic));
|
||||
validate(name, Topic) when is_binary(Topic) ->
|
||||
Words = words(Topic),
|
||||
validate2(Words) and (not wildcard(Words)).
|
||||
validate2(Words)
|
||||
andalso (not wildcard(Words))
|
||||
orelse error(topic_name_error).
|
||||
|
||||
validate2([]) ->
|
||||
true;
|
||||
|
@ -129,7 +129,7 @@ validate3(<<C/utf8, _Rest/binary>>) when C == $#; C == $+; C == 0 ->
|
|||
validate3(<<_/utf8, Rest/binary>>) ->
|
||||
validate3(Rest).
|
||||
|
||||
%% @doc Topic to triples
|
||||
%% @doc Topic to triples.
|
||||
-spec(triples(topic()) -> list(triple())).
|
||||
triples(Topic) when is_binary(Topic) ->
|
||||
triples(words(Topic), root, []).
|
||||
|
@ -218,13 +218,14 @@ parse(TopicFilter) when is_binary(TopicFilter) ->
|
|||
parse({TopicFilter, Options}) when is_binary(TopicFilter) ->
|
||||
parse(TopicFilter, Options).
|
||||
|
||||
-spec(parse(topic(), map()) -> {topic(), map()}).
|
||||
parse(TopicFilter = <<"$queue/", _/binary>>, #{share := _Group}) ->
|
||||
error({invalid_topic_filter, TopicFilter});
|
||||
parse(TopicFilter = <<?SHARE, "/", _/binary>>, #{share := _Group}) ->
|
||||
parse(TopicFilter = <<"$share/", _/binary>>, #{share := _Group}) ->
|
||||
error({invalid_topic_filter, TopicFilter});
|
||||
parse(<<"$queue/", TopicFilter/binary>>, Options) ->
|
||||
parse(TopicFilter, Options#{share => <<"$queue">>});
|
||||
parse(TopicFilter = <<?SHARE, "/", Rest/binary>>, Options) ->
|
||||
parse(TopicFilter = <<"$share/", Rest/binary>>, Options) ->
|
||||
case binary:split(Rest, <<"/">>) of
|
||||
[_Any] -> error({invalid_topic_filter, TopicFilter});
|
||||
[ShareName, Filter] ->
|
||||
|
|
|
@ -1,206 +0,0 @@
|
|||
%% Copyright (c) 2013-2019 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_SUITE).
|
||||
|
||||
-compile(export_all).
|
||||
-compile(nowarn_export_all).
|
||||
|
||||
-define(APP, emqx).
|
||||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
-include_lib("common_test/include/ct.hrl").
|
||||
|
||||
-include("emqx_mqtt.hrl").
|
||||
|
||||
-record(ssl_socket, {tcp, ssl}).
|
||||
|
||||
|
||||
-define(CLIENT, ?CONNECT_PACKET(#mqtt_packet_connect{
|
||||
client_id = <<"mqtt_client">>,
|
||||
username = <<"admin">>,
|
||||
password = <<"public">>})).
|
||||
|
||||
-define(CLIENT2, ?CONNECT_PACKET(#mqtt_packet_connect{
|
||||
username = <<"admin">>,
|
||||
clean_start = false,
|
||||
password = <<"public">>})).
|
||||
|
||||
-define(CLIENT3, ?CONNECT_PACKET(#mqtt_packet_connect{
|
||||
username = <<"admin">>,
|
||||
proto_ver = ?MQTT_PROTO_V5,
|
||||
clean_start = false,
|
||||
password = <<"public">>,
|
||||
will_props = #{'Will-Delay-Interval' => 2}})).
|
||||
|
||||
-define(SUBCODE, [0]).
|
||||
|
||||
-define(PACKETID, 1).
|
||||
|
||||
-define(PUBQOS, 1).
|
||||
|
||||
-define(SUBPACKET, ?SUBSCRIBE_PACKET(?PACKETID, [{<<"sub/topic">>, ?DEFAULT_SUBOPTS}])).
|
||||
|
||||
-define(PUBPACKET, ?PUBLISH_PACKET(?PUBQOS, <<"sub/topic">>, ?PACKETID, <<"publish">>)).
|
||||
|
||||
-define(PAYLOAD, [{type,"dsmSimulationData"},
|
||||
{id, 9999},
|
||||
{status, "running"},
|
||||
{soc, 1536702170},
|
||||
{fracsec, 451000},
|
||||
{data, lists:seq(1, 20480)}]).
|
||||
|
||||
-define(BIG_PUBPACKET, ?PUBLISH_PACKET(?PUBQOS, <<"sub/topic">>, ?PACKETID, emqx_json:encode(?PAYLOAD))).
|
||||
|
||||
all() ->
|
||||
[{group, connect},
|
||||
{group, publish}].
|
||||
|
||||
groups() ->
|
||||
[{connect, [non_parallel_tests],
|
||||
[mqtt_connect,
|
||||
mqtt_connect_with_tcp,
|
||||
mqtt_connect_with_will_props,
|
||||
mqtt_connect_with_ssl_oneway,
|
||||
mqtt_connect_with_ssl_twoway,
|
||||
mqtt_connect_with_ws]},
|
||||
{publish, [non_parallel_tests],
|
||||
[packet_size]}].
|
||||
|
||||
init_per_suite(Config) ->
|
||||
emqx_ct_helpers:start_apps([]),
|
||||
Config.
|
||||
|
||||
end_per_suite(_Config) ->
|
||||
emqx_ct_helpers:stop_apps([]).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Protocol Test
|
||||
%%--------------------------------------------------------------------
|
||||
mqtt_connect(_) ->
|
||||
%% Issue #599
|
||||
%% Empty clientId and clean_session = false
|
||||
?assertEqual(<<32,2,0,2>>, connect_broker_(<<16,12,0,4,77,81,84,84,4,0,0,90,0,0>>, 4)),
|
||||
%% Empty clientId and clean_session = true
|
||||
?assertEqual(<<32,2,0,0>>, connect_broker_(<<16,12,0,4,77,81,84,84,4,2,0,90,0,0>>, 4)).
|
||||
|
||||
connect_broker_(Packet, RecvSize) ->
|
||||
{ok, Sock} = emqx_client_sock:connect({127,0,0,1}, 1883, [binary, {packet, raw}, {active, false}], 3000),
|
||||
emqx_client_sock:send(Sock, Packet),
|
||||
{ok, Data} = gen_tcp:recv(Sock, RecvSize, 3000),
|
||||
emqx_client_sock:close(Sock),
|
||||
Data.
|
||||
|
||||
mqtt_connect_with_tcp(_) ->
|
||||
%% Issue #599
|
||||
%% Empty clientId and clean_session = false
|
||||
{ok, Sock} = emqx_client_sock:connect({127,0,0,1}, 1883, [binary, {packet, raw}, {active, false}], 3000),
|
||||
Packet = raw_send_serialize(?CLIENT2),
|
||||
emqx_client_sock:send(Sock, Packet),
|
||||
{ok, Data} = gen_tcp:recv(Sock, 0),
|
||||
{ok, ?CONNACK_PACKET(?CONNACK_INVALID_ID), <<>>, _} = raw_recv_pase(Data),
|
||||
emqx_client_sock:close(Sock).
|
||||
|
||||
mqtt_connect_with_will_props(_) ->
|
||||
%% Issue #599
|
||||
%% Empty clientId and clean_session = false
|
||||
{ok, Sock} = emqx_client_sock:connect({127,0,0,1}, 1883, [binary, {packet, raw}, {active, false}], 3000),
|
||||
Packet = raw_send_serialize(?CLIENT3),
|
||||
emqx_client_sock:send(Sock, Packet),
|
||||
emqx_client_sock:close(Sock).
|
||||
|
||||
mqtt_connect_with_ssl_oneway(_) ->
|
||||
emqx:shutdown(),
|
||||
emqx_ct_helpers:change_emqx_opts(ssl_oneway),
|
||||
emqx:start(),
|
||||
ClientSsl = emqx_ct_helpers:client_ssl(),
|
||||
{ok, #ssl_socket{tcp = _Sock1, ssl = SslSock} = Sock}
|
||||
= emqx_client_sock:connect("127.0.0.1", 8883, [{ssl_opts, ClientSsl}], 3000),
|
||||
Packet = raw_send_serialize(?CLIENT),
|
||||
emqx_client_sock:setopts(Sock, [{active, once}]),
|
||||
emqx_client_sock:send(Sock, Packet),
|
||||
?assert(
|
||||
receive {ssl, _, ConAck}->
|
||||
{ok, ?CONNACK_PACKET(?CONNACK_ACCEPT), <<>>, _} = raw_recv_pase(ConAck), true
|
||||
after 1000 ->
|
||||
false
|
||||
end),
|
||||
ssl:close(SslSock).
|
||||
|
||||
mqtt_connect_with_ssl_twoway(_Config) ->
|
||||
emqx:shutdown(),
|
||||
emqx_ct_helpers:change_emqx_opts(ssl_twoway),
|
||||
emqx:start(),
|
||||
ClientSsl = emqx_ct_helpers:client_ssl_twoway(),
|
||||
{ok, #ssl_socket{tcp = _Sock1, ssl = SslSock} = Sock}
|
||||
= emqx_client_sock:connect("127.0.0.1", 8883, [{ssl_opts, ClientSsl}], 3000),
|
||||
Packet = raw_send_serialize(?CLIENT),
|
||||
emqx_client_sock:setopts(Sock, [{active, once}]),
|
||||
emqx_client_sock:send(Sock, Packet),
|
||||
timer:sleep(500),
|
||||
?assert(
|
||||
receive {ssl, _, Data}->
|
||||
{ok, ?CONNACK_PACKET(?CONNACK_ACCEPT), <<>>, _} = raw_recv_pase(Data), true
|
||||
after 1000 ->
|
||||
false
|
||||
end),
|
||||
ssl:close(SslSock),
|
||||
emqx_client_sock:close(Sock).
|
||||
|
||||
mqtt_connect_with_ws(_Config) ->
|
||||
WS = rfc6455_client:new("ws://127.0.0.1:8083" ++ "/mqtt", self()),
|
||||
{ok, _} = rfc6455_client:open(WS),
|
||||
|
||||
%% Connect Packet
|
||||
Packet = raw_send_serialize(?CLIENT),
|
||||
ok = rfc6455_client:send_binary(WS, Packet),
|
||||
{binary, CONACK} = rfc6455_client:recv(WS),
|
||||
{ok, ?CONNACK_PACKET(?CONNACK_ACCEPT), <<>>, _} = raw_recv_pase(CONACK),
|
||||
|
||||
%% Sub Packet
|
||||
SubPacket = raw_send_serialize(?SUBPACKET),
|
||||
rfc6455_client:send_binary(WS, SubPacket),
|
||||
{binary, SubAck} = rfc6455_client:recv(WS),
|
||||
{ok, ?SUBACK_PACKET(?PACKETID, ?SUBCODE), <<>>, _} = raw_recv_pase(SubAck),
|
||||
|
||||
%% Pub Packet QoS 1
|
||||
PubPacket = raw_send_serialize(?PUBPACKET),
|
||||
rfc6455_client:send_binary(WS, PubPacket),
|
||||
{binary, PubAck} = rfc6455_client:recv(WS),
|
||||
{ok, ?PUBACK_PACKET(?PACKETID), <<>>, _} = raw_recv_pase(PubAck),
|
||||
{close, _} = rfc6455_client:close(WS),
|
||||
ok.
|
||||
|
||||
%%issue 1811
|
||||
packet_size(_Config) ->
|
||||
{ok, Sock} = emqx_client_sock:connect({127,0,0,1}, 1883, [binary, {packet, raw}, {active, false}], 3000),
|
||||
Packet = raw_send_serialize(?CLIENT),
|
||||
emqx_client_sock:send(Sock, Packet),
|
||||
{ok, Data} = gen_tcp:recv(Sock, 0),
|
||||
{ok, ?CONNACK_PACKET(?CONNACK_ACCEPT), <<>>, _} = raw_recv_pase(Data),
|
||||
|
||||
%% Pub Packet QoS 1
|
||||
PubPacket = raw_send_serialize(?BIG_PUBPACKET),
|
||||
emqx_client_sock:send(Sock, PubPacket),
|
||||
{ok, Data1} = gen_tcp:recv(Sock, 0),
|
||||
{ok, ?PUBACK_PACKET(?PACKETID), <<>>, _} = raw_recv_pase(Data1),
|
||||
emqx_client_sock:close(Sock).
|
||||
|
||||
raw_send_serialize(Packet) ->
|
||||
emqx_frame:serialize(Packet).
|
||||
|
||||
raw_recv_pase(Bin) ->
|
||||
emqx_frame:parse(Bin).
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%%
|
||||
%% [ACL](https://github.com/emqtt/emqttd/wiki/ACL)
|
||||
%%
|
||||
%% -type who() :: all | binary() |
|
||||
%% {ipaddr, esockd_access:cidr()} |
|
||||
%% {client, binary()} |
|
||||
%% {user, binary()}.
|
||||
%%
|
||||
%% -type access() :: subscribe | publish | pubsub.
|
||||
%%
|
||||
%% -type topic() :: binary().
|
||||
%%
|
||||
%% -type rule() :: {allow, all} |
|
||||
%% {allow, who(), access(), list(topic())} |
|
||||
%% {deny, all} |
|
||||
%% {deny, who(), access(), list(topic())}.
|
||||
%%
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
{allow, {user, "dashboard"}, subscribe, ["$SYS/#"]}.
|
||||
|
||||
{allow, {ipaddr, "127.0.0.1"}, pubsub, ["$SYS/#", "#"]}.
|
||||
|
||||
{deny, all, subscribe, ["$SYS/#", {eq, "#"}]}.
|
||||
|
||||
{allow, all}.
|
||||
|
||||
|
|
@ -21,41 +21,46 @@
|
|||
|
||||
-include("emqx.hrl").
|
||||
|
||||
-include_lib("common_test/include/ct.hrl").
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
-include_lib("common_test/include/ct.hrl").
|
||||
|
||||
-define(AC, emqx_access_control).
|
||||
-define(CACHE, emqx_acl_cache).
|
||||
|
||||
-import(emqx_access_rule, [compile/1, match/3]).
|
||||
-import(emqx_access_rule,
|
||||
[ compile/1
|
||||
, match/3
|
||||
]).
|
||||
|
||||
all() ->
|
||||
[{group, access_control},
|
||||
{group, acl_cache},
|
||||
{group, access_control_cache_mode},
|
||||
{group, access_rule}].
|
||||
{group, access_rule}
|
||||
].
|
||||
|
||||
groups() ->
|
||||
[{access_control, [sequence],
|
||||
[reload_acl,
|
||||
check_acl_1,
|
||||
check_acl_2]},
|
||||
{access_control_cache_mode, [],
|
||||
[acl_cache_basic,
|
||||
acl_cache_expiry,
|
||||
acl_cache_cleanup,
|
||||
acl_cache_full]},
|
||||
{acl_cache, [],
|
||||
[put_get_del_cache,
|
||||
cache_update,
|
||||
cache_expiry,
|
||||
cache_replacement,
|
||||
cache_cleanup,
|
||||
cache_auto_emtpy,
|
||||
cache_auto_cleanup]},
|
||||
{access_rule, [],
|
||||
[compile_rule,
|
||||
match_rule]}].
|
||||
[t_reload_acl,
|
||||
t_check_acl_1,
|
||||
t_check_acl_2]},
|
||||
{access_control_cache_mode, [sequence],
|
||||
[t_acl_cache_basic,
|
||||
t_acl_cache_expiry,
|
||||
t_acl_cache_cleanup,
|
||||
t_acl_cache_full]},
|
||||
{acl_cache, [sequence],
|
||||
[t_put_get_del_cache,
|
||||
t_cache_update,
|
||||
t_cache_expiry,
|
||||
t_cache_replacement,
|
||||
t_cache_cleanup,
|
||||
t_cache_auto_emtpy,
|
||||
t_cache_auto_cleanup]},
|
||||
{access_rule, [parallel],
|
||||
[t_compile_rule,
|
||||
t_match_rule]
|
||||
}].
|
||||
|
||||
init_per_suite(Config) ->
|
||||
emqx_ct_helpers:start_apps([]),
|
||||
|
@ -102,62 +107,76 @@ end_per_group(_Group, Config) ->
|
|||
%% emqx_access_control
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
reload_acl(_) ->
|
||||
t_reload_acl(_) ->
|
||||
ok = ?AC:reload_acl().
|
||||
|
||||
check_acl_1(_) ->
|
||||
SelfUser = #{client_id => <<"client1">>, username => <<"testuser">>, zone => external},
|
||||
allow = ?AC:check_acl(SelfUser, subscribe, <<"users/testuser/1">>),
|
||||
allow = ?AC:check_acl(SelfUser, subscribe, <<"clients/client1">>),
|
||||
deny = ?AC:check_acl(SelfUser, subscribe, <<"clients/client1/x/y">>),
|
||||
allow = ?AC:check_acl(SelfUser, publish, <<"users/testuser/1">>),
|
||||
allow = ?AC:check_acl(SelfUser, subscribe, <<"a/b/c">>).
|
||||
check_acl_2(_) ->
|
||||
SelfUser = #{client_id => <<"client2">>, username => <<"xyz">>, zone => external},
|
||||
deny = ?AC:check_acl(SelfUser, subscribe, <<"a/b/c">>).
|
||||
t_check_acl_1(_) ->
|
||||
Client = #{zone => external,
|
||||
client_id => <<"client1">>,
|
||||
username => <<"testuser">>
|
||||
},
|
||||
allow = ?AC:check_acl(Client, subscribe, <<"users/testuser/1">>),
|
||||
allow = ?AC:check_acl(Client, subscribe, <<"clients/client1">>),
|
||||
deny = ?AC:check_acl(Client, subscribe, <<"clients/client1/x/y">>),
|
||||
allow = ?AC:check_acl(Client, publish, <<"users/testuser/1">>),
|
||||
allow = ?AC:check_acl(Client, subscribe, <<"a/b/c">>).
|
||||
|
||||
acl_cache_basic(_) ->
|
||||
SelfUser = #{client_id => <<"client1">>, username => <<"testuser">>, zone => external},
|
||||
t_check_acl_2(_) ->
|
||||
Client = #{zone => external,
|
||||
client_id => <<"client2">>,
|
||||
username => <<"xyz">>
|
||||
},
|
||||
deny = ?AC:check_acl(Client, subscribe, <<"a/b/c">>).
|
||||
|
||||
t_acl_cache_basic(_) ->
|
||||
Client = #{zone => external,
|
||||
client_id => <<"client1">>,
|
||||
username => <<"testuser">>
|
||||
},
|
||||
not_found = ?CACHE:get_acl_cache(subscribe, <<"users/testuser/1">>),
|
||||
not_found = ?CACHE:get_acl_cache(subscribe, <<"clients/client1">>),
|
||||
|
||||
allow = ?AC:check_acl(SelfUser, subscribe, <<"users/testuser/1">>),
|
||||
allow = ?AC:check_acl(SelfUser, subscribe, <<"clients/client1">>),
|
||||
allow = ?AC:check_acl(Client, subscribe, <<"users/testuser/1">>),
|
||||
allow = ?AC:check_acl(Client, subscribe, <<"clients/client1">>),
|
||||
|
||||
allow = ?CACHE:get_acl_cache(subscribe, <<"users/testuser/1">>),
|
||||
allow = ?CACHE:get_acl_cache(subscribe, <<"clients/client1">>),
|
||||
ok.
|
||||
allow = ?CACHE:get_acl_cache(subscribe, <<"clients/client1">>).
|
||||
|
||||
acl_cache_expiry(_) ->
|
||||
t_acl_cache_expiry(_) ->
|
||||
application:set_env(emqx, acl_cache_ttl, 100),
|
||||
SelfUser = #{client_id => <<"client1">>, username => <<"testuser">>, zone => external},
|
||||
allow = ?AC:check_acl(SelfUser, subscribe, <<"clients/client1">>),
|
||||
Client = #{zone => external,
|
||||
client_id => <<"client1">>,
|
||||
username => <<"testuser">>
|
||||
},
|
||||
allow = ?AC:check_acl(Client, subscribe, <<"clients/client1">>),
|
||||
allow = ?CACHE:get_acl_cache(subscribe, <<"clients/client1">>),
|
||||
ct:sleep(150),
|
||||
not_found = ?CACHE:get_acl_cache(subscribe, <<"clients/client1">>),
|
||||
ok.
|
||||
not_found = ?CACHE:get_acl_cache(subscribe, <<"clients/client1">>).
|
||||
|
||||
acl_cache_full(_) ->
|
||||
t_acl_cache_full(_) ->
|
||||
application:set_env(emqx, acl_cache_max_size, 1),
|
||||
|
||||
SelfUser = #{client_id => <<"client1">>, username => <<"testuser">>, zone => external},
|
||||
allow = ?AC:check_acl(SelfUser, subscribe, <<"users/testuser/1">>),
|
||||
allow = ?AC:check_acl(SelfUser, subscribe, <<"clients/client1">>),
|
||||
Client = #{zone => external,
|
||||
client_id => <<"client1">>,
|
||||
username => <<"testuser">>
|
||||
},
|
||||
allow = ?AC:check_acl(Client, subscribe, <<"users/testuser/1">>),
|
||||
allow = ?AC:check_acl(Client, subscribe, <<"clients/client1">>),
|
||||
|
||||
%% the older ones (the <<"users/testuser/1">>) will be evicted first
|
||||
not_found = ?CACHE:get_acl_cache(subscribe, <<"users/testuser/1">>),
|
||||
allow = ?CACHE:get_acl_cache(subscribe, <<"clients/client1">>),
|
||||
ok.
|
||||
allow = ?CACHE:get_acl_cache(subscribe, <<"clients/client1">>).
|
||||
|
||||
acl_cache_cleanup(_) ->
|
||||
t_acl_cache_cleanup(_) ->
|
||||
%% The acl cache will try to evict memory, if the size is full and the newest
|
||||
%% cache entry is expired
|
||||
application:set_env(emqx, acl_cache_ttl, 100),
|
||||
application:set_env(emqx, acl_cache_max_size, 2),
|
||||
|
||||
SelfUser = #{client_id => <<"client1">>, username => <<"testuser">>, zone => external},
|
||||
allow = ?AC:check_acl(SelfUser, subscribe, <<"users/testuser/1">>),
|
||||
allow = ?AC:check_acl(SelfUser, subscribe, <<"clients/client1">>),
|
||||
Client = #{zone => external,
|
||||
client_id => <<"client1">>,
|
||||
username => <<"testuser">>
|
||||
},
|
||||
allow = ?AC:check_acl(Client, subscribe, <<"users/testuser/1">>),
|
||||
allow = ?AC:check_acl(Client, subscribe, <<"clients/client1">>),
|
||||
|
||||
allow = ?CACHE:get_acl_cache(subscribe, <<"users/testuser/1">>),
|
||||
allow = ?CACHE:get_acl_cache(subscribe, <<"clients/client1">>),
|
||||
|
@ -166,14 +185,13 @@ acl_cache_cleanup(_) ->
|
|||
%% now the cache is full and the newest one - "clients/client1"
|
||||
%% should be expired, so we'll empty the cache before putting
|
||||
%% the next cache entry
|
||||
deny = ?AC:check_acl(SelfUser, subscribe, <<"#">>),
|
||||
deny = ?AC:check_acl(Client, subscribe, <<"#">>),
|
||||
|
||||
not_found = ?CACHE:get_acl_cache(subscribe, <<"users/testuser/1">>),
|
||||
not_found = ?CACHE:get_acl_cache(subscribe, <<"clients/client1">>),
|
||||
deny = ?CACHE:get_acl_cache(subscribe, <<"#">>),
|
||||
ok.
|
||||
deny = ?CACHE:get_acl_cache(subscribe, <<"#">>).
|
||||
|
||||
put_get_del_cache(_) ->
|
||||
t_put_get_del_cache(_) ->
|
||||
application:set_env(emqx, acl_cache_ttl, 300000),
|
||||
application:set_env(emqx, acl_cache_max_size, 30),
|
||||
|
||||
|
@ -188,7 +206,7 @@ put_get_del_cache(_) ->
|
|||
2 = ?CACHE:get_cache_size(),
|
||||
?assertEqual(?CACHE:cache_k(subscribe, <<"b">>), ?CACHE:get_newest_key()).
|
||||
|
||||
cache_expiry(_) ->
|
||||
t_cache_expiry(_) ->
|
||||
application:set_env(emqx, acl_cache_ttl, 100),
|
||||
application:set_env(emqx, acl_cache_max_size, 30),
|
||||
ok = ?CACHE:put_acl_cache(subscribe, <<"a">>, allow),
|
||||
|
@ -203,7 +221,7 @@ cache_expiry(_) ->
|
|||
ct:sleep(150),
|
||||
not_found = ?CACHE:get_acl_cache(subscribe, <<"a">>).
|
||||
|
||||
cache_update(_) ->
|
||||
t_cache_update(_) ->
|
||||
application:set_env(emqx, acl_cache_ttl, 300000),
|
||||
application:set_env(emqx, acl_cache_max_size, 30),
|
||||
[] = ?CACHE:dump_acl_cache(),
|
||||
|
@ -222,7 +240,7 @@ cache_update(_) ->
|
|||
?assertEqual(?CACHE:cache_k(publish, <<"b">>), ?CACHE:get_newest_key()),
|
||||
?assertEqual(?CACHE:cache_k(subscribe, <<"a">>), ?CACHE:get_oldest_key()).
|
||||
|
||||
cache_replacement(_) ->
|
||||
t_cache_replacement(_) ->
|
||||
application:set_env(emqx, acl_cache_ttl, 300000),
|
||||
application:set_env(emqx, acl_cache_max_size, 3),
|
||||
ok = ?CACHE:put_acl_cache(subscribe, <<"a">>, allow),
|
||||
|
@ -248,7 +266,7 @@ cache_replacement(_) ->
|
|||
not_found = ?CACHE:get_acl_cache(publish, <<"b">>),
|
||||
allow = ?CACHE:get_acl_cache(publish, <<"c">>).
|
||||
|
||||
cache_cleanup(_) ->
|
||||
t_cache_cleanup(_) ->
|
||||
application:set_env(emqx, acl_cache_ttl, 100),
|
||||
application:set_env(emqx, acl_cache_max_size, 30),
|
||||
ok = ?CACHE:put_acl_cache(subscribe, <<"a">>, allow),
|
||||
|
@ -261,7 +279,7 @@ cache_cleanup(_) ->
|
|||
?assertEqual(?CACHE:cache_k(publish, <<"c">>), ?CACHE:get_oldest_key()),
|
||||
1 = ?CACHE:get_cache_size().
|
||||
|
||||
cache_auto_emtpy(_) ->
|
||||
t_cache_auto_emtpy(_) ->
|
||||
%% verify cache is emptied when cache full and even the newest
|
||||
%% one is expired.
|
||||
application:set_env(emqx, acl_cache_ttl, 100),
|
||||
|
@ -275,7 +293,7 @@ cache_auto_emtpy(_) ->
|
|||
ok = ?CACHE:put_acl_cache(subscribe, <<"d">>, deny),
|
||||
1 = ?CACHE:get_cache_size().
|
||||
|
||||
cache_auto_cleanup(_) ->
|
||||
t_cache_auto_cleanup(_) ->
|
||||
%% verify we'll cleanup expired entries when we got a exipired acl
|
||||
%% from cache.
|
||||
application:set_env(emqx, acl_cache_ttl, 100),
|
||||
|
@ -299,7 +317,7 @@ cache_auto_cleanup(_) ->
|
|||
%% emqx_access_rule
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
compile_rule(_) ->
|
||||
t_compile_rule(_) ->
|
||||
{allow, {'and', [{ipaddr, {{127,0,0,1}, {127,0,0,1}, 32}},
|
||||
{user, <<"user">>}]}, subscribe, [ [<<"$SYS">>, '#'], ['#'] ]} =
|
||||
compile({allow, {'and', [{ipaddr, "127.0.0.1"}, {user, <<"user">>}]}, subscribe, ["$SYS/#", "#"]}),
|
||||
|
@ -324,23 +342,36 @@ compile_rule(_) ->
|
|||
{allow, all} = compile({allow, all}),
|
||||
{deny, all} = compile({deny, all}).
|
||||
|
||||
match_rule(_) ->
|
||||
User = #{client_id => <<"testClient">>, username => <<"TestUser">>, peername => {{127,0,0,1}, 2948}, zone => external},
|
||||
User2 = #{client_id => <<"testClient">>, username => <<"TestUser">>, peername => {{192,168,0,10}, 3028}, zone => external},
|
||||
|
||||
{matched, allow} = match(User, <<"Test/Topic">>, {allow, all}),
|
||||
{matched, deny} = match(User, <<"Test/Topic">>, {deny, all}),
|
||||
{matched, allow} = match(User, <<"Test/Topic">>, compile({allow, {ipaddr, "127.0.0.1"}, subscribe, ["$SYS/#", "#"]})),
|
||||
{matched, allow} = match(User2, <<"Test/Topic">>, compile({allow, {ipaddr, "192.168.0.1/24"}, subscribe, ["$SYS/#", "#"]})),
|
||||
{matched, allow} = match(User, <<"d/e/f/x">>, compile({allow, {user, "TestUser"}, subscribe, ["a/b/c", "d/e/f/#"]})),
|
||||
nomatch = match(User, <<"d/e/f/x">>, compile({allow, {user, "admin"}, pubsub, ["d/e/f/#"]})),
|
||||
{matched, allow} = match(User, <<"testTopics/testClient">>, compile({allow, {client, "testClient"}, publish, ["testTopics/testClient"]})),
|
||||
{matched, allow} = match(User, <<"clients/testClient">>, compile({allow, all, pubsub, ["clients/%c"]})),
|
||||
{matched, allow} = match(#{username => <<"user2">>}, <<"users/user2/abc/def">>, compile({allow, all, subscribe, ["users/%u/#"]})),
|
||||
{matched, deny} = match(User, <<"d/e/f">>, compile({deny, all, subscribe, ["$SYS/#", "#"]})),
|
||||
t_match_rule(_) ->
|
||||
Client1 = #{zone => external,
|
||||
client_id => <<"testClient">>,
|
||||
username => <<"TestUser">>,
|
||||
peername => {{127,0,0,1}, 2948}
|
||||
},
|
||||
Client2 = #{zone => external,
|
||||
client_id => <<"testClient">>,
|
||||
username => <<"TestUser">>,
|
||||
peername => {{192,168,0,10}, 3028}
|
||||
},
|
||||
{matched, allow} = match(Client1, <<"Test/Topic">>, {allow, all}),
|
||||
{matched, deny} = match(Client1, <<"Test/Topic">>, {deny, all}),
|
||||
{matched, allow} = match(Client1, <<"Test/Topic">>,
|
||||
compile({allow, {ipaddr, "127.0.0.1"}, subscribe, ["$SYS/#", "#"]})),
|
||||
{matched, allow} = match(Client2, <<"Test/Topic">>,
|
||||
compile({allow, {ipaddr, "192.168.0.1/24"}, subscribe, ["$SYS/#", "#"]})),
|
||||
{matched, allow} = match(Client1, <<"d/e/f/x">>,
|
||||
compile({allow, {user, "TestUser"}, subscribe, ["a/b/c", "d/e/f/#"]})),
|
||||
nomatch = match(Client1, <<"d/e/f/x">>, compile({allow, {user, "admin"}, pubsub, ["d/e/f/#"]})),
|
||||
{matched, allow} = match(Client1, <<"testTopics/testClient">>,
|
||||
compile({allow, {client, "testClient"}, publish, ["testTopics/testClient"]})),
|
||||
{matched, allow} = match(Client1, <<"clients/testClient">>, compile({allow, all, pubsub, ["clients/%c"]})),
|
||||
{matched, allow} = match(#{username => <<"user2">>}, <<"users/user2/abc/def">>,
|
||||
compile({allow, all, subscribe, ["users/%u/#"]})),
|
||||
{matched, deny} = match(Client1, <<"d/e/f">>, compile({deny, all, subscribe, ["$SYS/#", "#"]})),
|
||||
Rule = compile({allow, {'and', [{ipaddr, "127.0.0.1"}, {user, <<"WrongUser">>}]}, publish, <<"Topic">>}),
|
||||
nomatch = match(User, <<"Topic">>, Rule),
|
||||
nomatch = match(Client1, <<"Topic">>, Rule),
|
||||
AndRule = compile({allow, {'and', [{ipaddr, "127.0.0.1"}, {user, <<"TestUser">>}]}, publish, <<"Topic">>}),
|
||||
{matched, allow} = match(User, <<"Topic">>, AndRule),
|
||||
{matched, allow} = match(Client1, <<"Topic">>, AndRule),
|
||||
OrRule = compile({allow, {'or', [{ipaddr, "127.0.0.1"}, {user, <<"WrongUser">>}]}, publish, ["Topic"]}),
|
||||
{matched, allow} = match(User, <<"Topic">>, OrRule).
|
||||
{matched, allow} = match(Client1, <<"Topic">>, OrRule).
|
||||
|
||||
|
|
|
@ -17,7 +17,11 @@
|
|||
-module(emqx_acl_test_mod).
|
||||
|
||||
%% ACL callbacks
|
||||
-export([init/1, check_acl/2, reload_acl/1, description/0]).
|
||||
-export([ init/1
|
||||
, check_acl/2
|
||||
, reload_acl/1
|
||||
, description/0
|
||||
]).
|
||||
|
||||
init(AclOpts) ->
|
||||
{ok, AclOpts}.
|
||||
|
|
|
@ -1,110 +0,0 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2019 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_alarm_handler_SUITE).
|
||||
|
||||
-compile(export_all).
|
||||
-compile(nowarn_export_all).
|
||||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
-include_lib("common_test/include/ct.hrl").
|
||||
|
||||
-include("emqx_mqtt.hrl").
|
||||
-include("emqx.hrl").
|
||||
|
||||
all() -> [t_alarm_handler].
|
||||
|
||||
init_per_suite(Config) ->
|
||||
emqx_ct_helpers:start_apps([], fun set_special_configs/1),
|
||||
Config.
|
||||
|
||||
end_per_suite(_Config) ->
|
||||
emqx_ct_helpers:stop_apps([]).
|
||||
|
||||
set_special_configs(emqx) ->
|
||||
application:set_env(emqx, acl_file, emqx_ct_helpers:deps_path(emqx, "test/emqx_access_SUITE_data/acl_deny_action.conf"));
|
||||
set_special_configs(_App) ->
|
||||
ok.
|
||||
|
||||
with_connection(DoFun) ->
|
||||
{ok, Sock} = emqx_client_sock:connect({127, 0, 0, 1}, 1883,
|
||||
[binary, {packet, raw}, {active, false}],
|
||||
3000),
|
||||
try
|
||||
DoFun(Sock)
|
||||
after
|
||||
emqx_client_sock:close(Sock)
|
||||
end.
|
||||
|
||||
t_alarm_handler(_) ->
|
||||
with_connection(
|
||||
fun(Sock) ->
|
||||
emqx_client_sock:send(Sock,
|
||||
raw_send_serialize(
|
||||
?CONNECT_PACKET(
|
||||
#mqtt_packet_connect{
|
||||
proto_ver = ?MQTT_PROTO_V5}),
|
||||
#{version => ?MQTT_PROTO_V5}
|
||||
)),
|
||||
{ok, Data} = gen_tcp:recv(Sock, 0),
|
||||
{ok, ?CONNACK_PACKET(?RC_SUCCESS), <<>>, _} = raw_recv_parse(Data, ?MQTT_PROTO_V5),
|
||||
|
||||
Topic1 = emqx_topic:systop(<<"alarms/alert">>),
|
||||
Topic2 = emqx_topic:systop(<<"alarms/clear">>),
|
||||
SubOpts = #{rh => 1, qos => ?QOS_2, rap => 0, nl => 0, rc => 0},
|
||||
emqx_client_sock:send(Sock,
|
||||
raw_send_serialize(
|
||||
?SUBSCRIBE_PACKET(
|
||||
1,
|
||||
[{Topic1, SubOpts},
|
||||
{Topic2, SubOpts}]),
|
||||
#{version => ?MQTT_PROTO_V5})),
|
||||
|
||||
{ok, Data2} = gen_tcp:recv(Sock, 0),
|
||||
{ok, ?SUBACK_PACKET(1, #{}, [2, 2]), <<>>, _} = raw_recv_parse(Data2, ?MQTT_PROTO_V5),
|
||||
|
||||
alarm_handler:set_alarm({alarm_for_test, #alarm{id = alarm_for_test,
|
||||
severity = error,
|
||||
title="alarm title",
|
||||
summary="alarm summary"}}),
|
||||
|
||||
{ok, Data3} = gen_tcp:recv(Sock, 0),
|
||||
|
||||
{ok, ?PUBLISH_PACKET(?QOS_0, Topic1, _, _), <<>>, _} = raw_recv_parse(Data3, ?MQTT_PROTO_V5),
|
||||
|
||||
?assertEqual(true, lists:keymember(alarm_for_test, 1, emqx_alarm_handler:get_alarms())),
|
||||
|
||||
alarm_handler:clear_alarm(alarm_for_test),
|
||||
|
||||
{ok, Data4} = gen_tcp:recv(Sock, 0),
|
||||
|
||||
{ok, ?PUBLISH_PACKET(?QOS_0, Topic2, _, _), <<>>, _} = raw_recv_parse(Data4, ?MQTT_PROTO_V5),
|
||||
|
||||
?assertEqual(false, lists:keymember(alarm_for_test, 1, emqx_alarm_handler:get_alarms()))
|
||||
|
||||
end).
|
||||
|
||||
raw_send_serialize(Packet) ->
|
||||
emqx_frame:serialize(Packet).
|
||||
|
||||
raw_send_serialize(Packet, Opts) ->
|
||||
emqx_frame:serialize(Packet, Opts).
|
||||
|
||||
raw_recv_parse(Bin, ProtoVer) ->
|
||||
emqx_frame:parse(Bin, {none, #{max_size => ?MAX_PACKET_SIZE,
|
||||
version => ProtoVer}}).
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2019 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_auth_anonymous_test_mod).
|
||||
|
||||
%% ACL callbacks
|
||||
-export([init/1, check/3, description/0]).
|
||||
|
||||
init(AclOpts) ->
|
||||
{ok, AclOpts}.
|
||||
|
||||
check(_Client, _Password, _Opts) ->
|
||||
allow.
|
||||
|
||||
description() ->
|
||||
"Test emqx_auth_anonymous Mod".
|
|
@ -1,30 +0,0 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2019 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_auth_dashboard).
|
||||
|
||||
%% Auth callbacks
|
||||
-export([init/1, check/3, description/0]).
|
||||
|
||||
init(Opts) ->
|
||||
{ok, Opts}.
|
||||
|
||||
check(_Client, _Password, _Opts) ->
|
||||
allow.
|
||||
|
||||
description() ->
|
||||
"Test Auth Mod".
|
||||
|
|
@ -20,35 +20,63 @@
|
|||
-compile(nowarn_export_all).
|
||||
|
||||
-include("emqx.hrl").
|
||||
-include("emqx_mqtt.hrl").
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
all() -> [t_banned_all].
|
||||
all() -> emqx_ct:all(?MODULE).
|
||||
|
||||
t_banned_all(_) ->
|
||||
emqx_ct_helpers:start_apps([]),
|
||||
emqx_banned:start_link(),
|
||||
TimeNow = erlang:system_time(second),
|
||||
init_per_suite(Config) ->
|
||||
application:load(emqx),
|
||||
ok = ekka:start(),
|
||||
Config.
|
||||
|
||||
end_per_suite(_Config) ->
|
||||
ekka:stop(),
|
||||
ekka_mnesia:ensure_stopped(),
|
||||
ekka_mnesia:delete_schema().
|
||||
|
||||
t_add_delete(_) ->
|
||||
Banned = #banned{who = {client_id, <<"TestClient">>},
|
||||
reason = <<"test">>,
|
||||
by = <<"banned suite">>,
|
||||
desc = <<"test">>,
|
||||
until = TimeNow + 1},
|
||||
until = erlang:system_time(second) + 1000
|
||||
},
|
||||
ok = emqx_banned:add(Banned),
|
||||
% here is not expire banned test because its check interval is greater than 5 mins, but its effect has been confirmed
|
||||
?assert(emqx_banned:check(#{client_id => <<"TestClient">>,
|
||||
username => undefined,
|
||||
peername => {undefined, undefined}})),
|
||||
timer:sleep(2500),
|
||||
?assertNot(emqx_banned:check(#{client_id => <<"TestClient">>,
|
||||
username => undefined,
|
||||
peername => {undefined, undefined}})),
|
||||
ok = emqx_banned:add(Banned),
|
||||
?assert(emqx_banned:check(#{client_id => <<"TestClient">>,
|
||||
username => undefined,
|
||||
peername => {undefined, undefined}})),
|
||||
emqx_banned:delete({client_id, <<"TestClient">>}),
|
||||
?assertNot(emqx_banned:check(#{client_id => <<"TestClient">>,
|
||||
username => undefined,
|
||||
peername => {undefined, undefined}})),
|
||||
emqx_ct_helpers:stop_apps([]).
|
||||
?assertEqual(1, emqx_banned:info(size)),
|
||||
ok = emqx_banned:delete({client_id, <<"TestClient">>}),
|
||||
?assertEqual(0, emqx_banned:info(size)).
|
||||
|
||||
t_check(_) ->
|
||||
ok = emqx_banned:add(#banned{who = {client_id, <<"BannedClient">>}}),
|
||||
ok = emqx_banned:add(#banned{who = {username, <<"BannedUser">>}}),
|
||||
ok = emqx_banned:add(#banned{who = {ipaddr, {192,168,0,1}}}),
|
||||
?assertEqual(3, emqx_banned:info(size)),
|
||||
Client1 = #{client_id => <<"BannedClient">>,
|
||||
username => <<"user">>,
|
||||
peername => {{127,0,0,1}, 5000}
|
||||
},
|
||||
Client2 = #{client_id => <<"client">>,
|
||||
username => <<"BannedUser">>,
|
||||
peername => {{127,0,0,1}, 5000}
|
||||
},
|
||||
Client3 = #{client_id => <<"client">>,
|
||||
username => <<"user">>,
|
||||
peername => {{192,168,0,1}, 5000}
|
||||
},
|
||||
Client4 = #{client_id => <<"client">>,
|
||||
username => <<"user">>,
|
||||
peername => {{127,0,0,1}, 5000}
|
||||
},
|
||||
?assert(emqx_banned:check(Client1)),
|
||||
?assert(emqx_banned:check(Client2)),
|
||||
?assert(emqx_banned:check(Client3)),
|
||||
?assertNot(emqx_banned:check(Client4)),
|
||||
ok = emqx_banned:delete({client_id, <<"BannedClient">>}),
|
||||
ok = emqx_banned:delete({username, <<"BannedUser">>}),
|
||||
ok = emqx_banned:delete({ipaddr, {192,168,0,1}}),
|
||||
?assertNot(emqx_banned:check(Client1)),
|
||||
?assertNot(emqx_banned:check(Client2)),
|
||||
?assertNot(emqx_banned:check(Client3)),
|
||||
?assertNot(emqx_banned:check(Client4)),
|
||||
?assertEqual(0, emqx_banned:info(size)).
|
||||
|
||||
|
|
|
@ -21,11 +21,13 @@
|
|||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
all() ->
|
||||
[batch_full_commit, batch_linger_commit].
|
||||
all() -> emqx_ct:all(?MODULE).
|
||||
|
||||
batch_full_commit(_) ->
|
||||
B0 = emqx_batch:init(#{batch_size => 3, linger_ms => 2000, commit_fun => fun(_) -> ok end}),
|
||||
t_batch_full_commit(_) ->
|
||||
B0 = emqx_batch:init(#{batch_size => 3,
|
||||
linger_ms => 2000,
|
||||
commit_fun => fun(_) -> ok end
|
||||
}),
|
||||
B3 = lists:foldl(fun(E, B) -> emqx_batch:push(E, B) end, B0, [a, b, c]),
|
||||
?assertEqual(3, emqx_batch:size(B3)),
|
||||
?assertEqual([a, b, c], emqx_batch:items(B3)),
|
||||
|
@ -34,9 +36,12 @@ batch_full_commit(_) ->
|
|||
?assertEqual(0, emqx_batch:size(B4)),
|
||||
?assertEqual([], emqx_batch:items(B4)).
|
||||
|
||||
batch_linger_commit(_) ->
|
||||
t_batch_linger_commit(_) ->
|
||||
CommitFun = fun(Q) -> ?assertEqual(3, length(Q)) end,
|
||||
B0 = emqx_batch:init(#{batch_size => 3, linger_ms => 500, commit_fun => CommitFun}),
|
||||
B0 = emqx_batch:init(#{batch_size => 3,
|
||||
linger_ms => 500,
|
||||
commit_fun => CommitFun
|
||||
}),
|
||||
B3 = lists:foldl(fun(E, B) -> emqx_batch:push(E, B) end, B0, [a, b, c]),
|
||||
?assertEqual(3, emqx_batch:size(B3)),
|
||||
?assertEqual([a, b, c], emqx_batch:items(B3)),
|
||||
|
|
|
@ -29,19 +29,24 @@
|
|||
|
||||
all() ->
|
||||
[{group, pubsub},
|
||||
{group, session},
|
||||
{group, metrics},
|
||||
{group, stats}].
|
||||
|
||||
groups() ->
|
||||
[{pubsub, [sequence], [subscribe_unsubscribe,
|
||||
publish, pubsub,
|
||||
t_shared_subscribe,
|
||||
dispatch_with_no_sub,
|
||||
'pubsub#', 'pubsub+']},
|
||||
{session, [sequence], [start_session]},
|
||||
{metrics, [sequence], [inc_dec_metric]},
|
||||
{stats, [sequence], [set_get_stat]}].
|
||||
[{pubsub, [sequence],
|
||||
[t_sub_unsub,
|
||||
t_publish,
|
||||
t_pubsub,
|
||||
t_shared_subscribe,
|
||||
t_dispatch_with_no_sub,
|
||||
't_pubsub#',
|
||||
't_pubsub+'
|
||||
]},
|
||||
{metrics, [sequence],
|
||||
[inc_dec_metric]},
|
||||
{stats, [sequence],
|
||||
[set_get_stat]
|
||||
}].
|
||||
|
||||
init_per_suite(Config) ->
|
||||
emqx_ct_helpers:start_apps([]),
|
||||
|
@ -54,47 +59,48 @@ end_per_suite(_Config) ->
|
|||
%% PubSub Test
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
subscribe_unsubscribe(_) ->
|
||||
ok = emqx:subscribe(<<"topic">>, <<"clientId">>),
|
||||
ok = emqx:subscribe(<<"topic/1">>, <<"clientId">>, #{ qos => 1 }),
|
||||
ok = emqx:subscribe(<<"topic/2">>, <<"clientId">>, #{ qos => 2 }),
|
||||
true = emqx:subscribed(<<"clientId">>, <<"topic">>),
|
||||
Topics = emqx:topics(),
|
||||
t_sub_unsub(_) ->
|
||||
ok = emqx_broker:subscribe(<<"topic">>, <<"clientId">>),
|
||||
ok = emqx_broker:subscribe(<<"topic/1">>, <<"clientId">>, #{qos => 1}),
|
||||
ok = emqx_broker:subscribe(<<"topic/2">>, <<"clientId">>, #{qos => 2}),
|
||||
true = emqx_broker:subscribed(<<"clientId">>, <<"topic">>),
|
||||
Topics = emqx_broker:topics(),
|
||||
lists:foreach(fun(Topic) ->
|
||||
?assert(lists:member(Topic, Topics))
|
||||
end, Topics),
|
||||
ok = emqx:unsubscribe(<<"topic">>),
|
||||
ok = emqx:unsubscribe(<<"topic/1">>),
|
||||
ok = emqx:unsubscribe(<<"topic/2">>).
|
||||
ok = emqx_broker:unsubscribe(<<"topic">>),
|
||||
ok = emqx_broker:unsubscribe(<<"topic/1">>),
|
||||
ok = emqx_broker:unsubscribe(<<"topic/2">>).
|
||||
|
||||
publish(_) ->
|
||||
t_publish(_) ->
|
||||
Msg = emqx_message:make(ct, <<"test/pubsub">>, <<"hello">>),
|
||||
ok = emqx:subscribe(<<"test/+">>),
|
||||
ok = emqx_broker:subscribe(<<"test/+">>),
|
||||
timer:sleep(10),
|
||||
emqx:publish(Msg),
|
||||
?assert(receive {dispatch, <<"test/+">>, #message{payload = <<"hello">>}} -> true after 100 -> false end).
|
||||
emqx_broker:publish(Msg),
|
||||
?assert(receive {deliver, <<"test/+">>, #message{payload = <<"hello">>}} -> true after 100 -> false end).
|
||||
|
||||
dispatch_with_no_sub(_) ->
|
||||
t_dispatch_with_no_sub(_) ->
|
||||
Msg = emqx_message:make(ct, <<"no_subscribers">>, <<"hello">>),
|
||||
Delivery = #delivery{sender = self(), message = Msg, results = []},
|
||||
?assertEqual(Delivery, emqx_broker:route([{<<"no_subscribers">>, node()}], Delivery)).
|
||||
|
||||
pubsub(_) ->
|
||||
t_pubsub(_) ->
|
||||
true = emqx:is_running(node()),
|
||||
Self = self(),
|
||||
Subscriber = <<"clientId">>,
|
||||
ok = emqx:subscribe(<<"a/b/c">>, Subscriber, #{ qos => 1 }),
|
||||
ok = emqx_broker:subscribe(<<"a/b/c">>, Subscriber, #{ qos => 1 }),
|
||||
#{qos := 1} = ets:lookup_element(emqx_suboption, {Self, <<"a/b/c">>}, 2),
|
||||
#{qos := 1} = emqx_broker:get_subopts(Subscriber, <<"a/b/c">>),
|
||||
true = emqx_broker:set_subopts(<<"a/b/c">>, #{qos => 0}),
|
||||
#{qos := 0} = emqx_broker:get_subopts(Subscriber, <<"a/b/c">>),
|
||||
ok = emqx:subscribe(<<"a/b/c">>, Subscriber, #{ qos => 2 }),
|
||||
ok = emqx_broker:subscribe(<<"a/b/c">>, Subscriber, #{ qos => 2 }),
|
||||
%% ct:log("Emq Sub: ~p.~n", [ets:lookup(emqx_suboption, {<<"a/b/c">>, Subscriber})]),
|
||||
timer:sleep(10),
|
||||
[Self] = emqx_broker:subscribers(<<"a/b/c">>),
|
||||
emqx:publish(emqx_message:make(ct, <<"a/b/c">>, <<"hello">>)),
|
||||
emqx_broker:publish(
|
||||
emqx_message:make(ct, <<"a/b/c">>, <<"hello">>)),
|
||||
?assert(
|
||||
receive {dispatch, <<"a/b/c">>, _ } ->
|
||||
receive {deliver, <<"a/b/c">>, _ } ->
|
||||
true;
|
||||
P ->
|
||||
ct:log("Receive Message: ~p~n",[P])
|
||||
|
@ -102,62 +108,43 @@ pubsub(_) ->
|
|||
false
|
||||
end),
|
||||
spawn(fun() ->
|
||||
emqx:subscribe(<<"a/b/c">>),
|
||||
emqx:subscribe(<<"c/d/e">>),
|
||||
emqx_broker:subscribe(<<"a/b/c">>),
|
||||
emqx_broker:subscribe(<<"c/d/e">>),
|
||||
timer:sleep(10),
|
||||
emqx:unsubscribe(<<"a/b/c">>)
|
||||
emqx_broker:unsubscribe(<<"a/b/c">>)
|
||||
end),
|
||||
timer:sleep(20),
|
||||
emqx:unsubscribe(<<"a/b/c">>).
|
||||
emqx_broker:unsubscribe(<<"a/b/c">>).
|
||||
|
||||
t_shared_subscribe(_) ->
|
||||
emqx:subscribe("$share/group2/topic2"),
|
||||
emqx:subscribe("$queue/topic3"),
|
||||
emqx_broker:subscribe(<<"$share/group2/topic2">>),
|
||||
emqx_broker:subscribe(<<"$queue/topic3">>),
|
||||
timer:sleep(10),
|
||||
ct:log("share subscriptions: ~p~n", [emqx:subscriptions(self())]),
|
||||
?assertEqual(2, length(emqx:subscriptions(self()))),
|
||||
emqx:unsubscribe("$share/group2/topic2"),
|
||||
emqx:unsubscribe("$queue/topic3"),
|
||||
?assertEqual(0, length(emqx:subscriptions(self()))).
|
||||
ct:pal("Share subscriptions: ~p",
|
||||
[emqx_broker:subscriptions(self())]),
|
||||
?assertEqual(2, length(emqx_broker:subscriptions(self()))),
|
||||
emqx_broker:unsubscribe(<<"$share/group2/topic2">>),
|
||||
emqx_broker:unsubscribe(<<"$queue/topic3">>),
|
||||
?assertEqual(0, length(emqx_broker:subscriptions(self()))).
|
||||
|
||||
'pubsub#'(_) ->
|
||||
emqx:subscribe(<<"a/#">>),
|
||||
't_pubsub#'(_) ->
|
||||
emqx_broker:subscribe(<<"a/#">>),
|
||||
timer:sleep(10),
|
||||
emqx:publish(emqx_message:make(ct, <<"a/b/c">>, <<"hello">>)),
|
||||
?assert(receive {dispatch, <<"a/#">>, _} -> true after 100 -> false end),
|
||||
emqx:unsubscribe(<<"a/#">>).
|
||||
emqx_broker:publish(emqx_message:make(ct, <<"a/b/c">>, <<"hello">>)),
|
||||
?assert(receive {deliver, <<"a/#">>, _} -> true after 100 -> false end),
|
||||
emqx_broker:unsubscribe(<<"a/#">>).
|
||||
|
||||
'pubsub+'(_) ->
|
||||
emqx:subscribe(<<"a/+/+">>),
|
||||
timer:sleep(10),
|
||||
emqx:publish(emqx_message:make(ct, <<"a/b/c">>, <<"hello">>)),
|
||||
?assert(receive {dispatch, <<"a/+/+">>, _} -> true after 100 -> false end),
|
||||
emqx:unsubscribe(<<"a/+/+">>).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Session Group
|
||||
%%--------------------------------------------------------------------
|
||||
start_session(_) ->
|
||||
ClientId = <<"clientId">>,
|
||||
{ok, ClientPid} = emqx_mock_client:start_link(ClientId),
|
||||
{ok, SessPid} = emqx_mock_client:open_session(ClientPid, ClientId, internal),
|
||||
Message1 = emqx_message:make(<<"clientId">>, 2, <<"topic">>, <<"hello">>),
|
||||
emqx_session:publish(SessPid, 1, Message1),
|
||||
emqx_session:pubrel(SessPid, 2, reasoncode),
|
||||
emqx_session:subscribe(SessPid, [{<<"topic/session">>, #{qos => 2}}]),
|
||||
Message2 = emqx_message:make(<<"clientId">>, 1, <<"topic/session">>, <<"test">>),
|
||||
emqx_session:publish(SessPid, 3, Message2),
|
||||
emqx_session:unsubscribe(SessPid, [{<<"topic/session">>, []}]),
|
||||
%% emqx_mock_client:stop(ClientPid).
|
||||
emqx_mock_client:close_session(ClientPid).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Broker Group
|
||||
%%--------------------------------------------------------------------
|
||||
't_pubsub+'(_) ->
|
||||
emqx_broker:subscribe(<<"a/+/+">>),
|
||||
timer:sleep(10), %% TODO: why sleep?
|
||||
emqx_broker:publish(emqx_message:make(ct, <<"a/b/c">>, <<"hello">>)),
|
||||
?assert(receive {deliver, <<"a/+/+">>, _} -> true after 100 -> false end),
|
||||
emqx_broker:unsubscribe(<<"a/+/+">>).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Metric Group
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
inc_dec_metric(_) ->
|
||||
emqx_metrics:inc('messages.retained', 10),
|
||||
emqx_metrics:dec('messages.retained', 10).
|
||||
|
@ -168,4 +155,5 @@ inc_dec_metric(_) ->
|
|||
|
||||
set_get_stat(_) ->
|
||||
emqx_stats:setstat('retained.max', 99),
|
||||
99 = emqx_stats:getstat('retained.max').
|
||||
?assertEqual(99, emqx_stats:getstat('retained.max')).
|
||||
|
||||
|
|
|
@ -1,74 +0,0 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2019 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_channel_SUITE).
|
||||
|
||||
-compile(export_all).
|
||||
-compile(nowarn_export_all).
|
||||
|
||||
-include("emqx_mqtt.hrl").
|
||||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
-include_lib("common_test/include/ct.hrl").
|
||||
|
||||
all() ->
|
||||
[t_connect_api].
|
||||
|
||||
init_per_suite(Config) ->
|
||||
emqx_ct_helpers:start_apps([]),
|
||||
Config.
|
||||
|
||||
end_per_suite(_Config) ->
|
||||
emqx_ct_helpers:stop_apps([]).
|
||||
|
||||
t_connect_api(_Config) ->
|
||||
{ok, T1} = emqx_client:start_link([{host, "localhost"},
|
||||
{client_id, <<"client1">>},
|
||||
{username, <<"testuser1">>},
|
||||
{password, <<"pass1">>}]),
|
||||
{ok, _} = emqx_client:connect(T1),
|
||||
CPid = emqx_cm:lookup_conn_pid(<<"client1">>),
|
||||
ConnStats = emqx_channel:stats(CPid),
|
||||
ok = t_stats(ConnStats),
|
||||
ConnAttrs = emqx_channel:attrs(CPid),
|
||||
ok = t_attrs(ConnAttrs),
|
||||
ConnInfo = emqx_channel:info(CPid),
|
||||
ok = t_info(ConnInfo),
|
||||
SessionPid = emqx_channel:session(CPid),
|
||||
true = is_pid(SessionPid),
|
||||
emqx_client:disconnect(T1).
|
||||
|
||||
t_info(ConnInfo) ->
|
||||
?assertEqual(tcp, maps:get(socktype, ConnInfo)),
|
||||
?assertEqual(running, maps:get(conn_state, ConnInfo)),
|
||||
?assertEqual(<<"client1">>, maps:get(client_id, ConnInfo)),
|
||||
?assertEqual(<<"testuser1">>, maps:get(username, ConnInfo)),
|
||||
?assertEqual(<<"MQTT">>, maps:get(proto_name, ConnInfo)).
|
||||
|
||||
t_attrs(AttrsData) ->
|
||||
?assertEqual(<<"client1">>, maps:get(client_id, AttrsData)),
|
||||
?assertEqual(emqx_channel, maps:get(conn_mod, AttrsData)),
|
||||
?assertEqual(<<"testuser1">>, maps:get(username, AttrsData)).
|
||||
|
||||
t_stats(StatsData) ->
|
||||
?assertEqual(true, proplists:get_value(recv_oct, StatsData) >= 0),
|
||||
?assertEqual(true, proplists:get_value(mailbox_len, StatsData) >= 0),
|
||||
?assertEqual(true, proplists:get_value(heap_size, StatsData) >= 0),
|
||||
?assertEqual(true, proplists:get_value(reductions, StatsData) >=0),
|
||||
?assertEqual(true, proplists:get_value(recv_pkt, StatsData) =:=1),
|
||||
?assertEqual(true, proplists:get_value(recv_msg, StatsData) >=0),
|
||||
?assertEqual(true, proplists:get_value(send_pkt, StatsData) =:=1).
|
|
@ -1,209 +0,0 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2019 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_client_SUITE).
|
||||
|
||||
-compile(export_all).
|
||||
-compile(nowarn_export_all).
|
||||
|
||||
-import(lists, [nth/2]).
|
||||
|
||||
-include("emqx_mqtt.hrl").
|
||||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
-include_lib("common_test/include/ct.hrl").
|
||||
|
||||
-define(TOPICS, [<<"TopicA">>, <<"TopicA/B">>, <<"Topic/C">>, <<"TopicA/C">>,
|
||||
<<"/TopicA">>]).
|
||||
|
||||
-define(WILD_TOPICS, [<<"TopicA/+">>, <<"+/C">>, <<"#">>, <<"/#">>, <<"/+">>,
|
||||
<<"+/+">>, <<"TopicA/#">>]).
|
||||
|
||||
all() ->
|
||||
[{group, mqttv4}].
|
||||
|
||||
groups() ->
|
||||
[{mqttv4, [non_parallel_tests],
|
||||
[basic_test,
|
||||
will_message_test,
|
||||
offline_message_queueing_test,
|
||||
overlapping_subscriptions_test,
|
||||
%% keepalive_test,
|
||||
redelivery_on_reconnect_test,
|
||||
%% subscribe_failure_test,
|
||||
dollar_topics_test]}].
|
||||
|
||||
init_per_suite(Config) ->
|
||||
emqx_ct_helpers:start_apps([]),
|
||||
Config.
|
||||
|
||||
end_per_suite(_Config) ->
|
||||
emqx_ct_helpers:stop_apps([]).
|
||||
|
||||
receive_messages(Count) ->
|
||||
receive_messages(Count, []).
|
||||
|
||||
receive_messages(0, Msgs) ->
|
||||
Msgs;
|
||||
receive_messages(Count, Msgs) ->
|
||||
receive
|
||||
{publish, Msg} ->
|
||||
receive_messages(Count-1, [Msg|Msgs]);
|
||||
_Other ->
|
||||
receive_messages(Count, Msgs)
|
||||
after 100 ->
|
||||
Msgs
|
||||
end.
|
||||
|
||||
basic_test(_Config) ->
|
||||
Topic = nth(1, ?TOPICS),
|
||||
ct:print("Basic test starting"),
|
||||
{ok, C} = emqx_client:start_link(),
|
||||
{ok, _} = emqx_client:connect(C),
|
||||
{ok, _, [1]} = emqx_client:subscribe(C, Topic, qos1),
|
||||
{ok, _, [2]} = emqx_client:subscribe(C, Topic, qos2),
|
||||
{ok, _} = emqx_client:publish(C, Topic, <<"qos 2">>, 2),
|
||||
{ok, _} = emqx_client:publish(C, Topic, <<"qos 2">>, 2),
|
||||
{ok, _} = emqx_client:publish(C, Topic, <<"qos 2">>, 2),
|
||||
?assertEqual(3, length(receive_messages(3))),
|
||||
ok = emqx_client:disconnect(C).
|
||||
|
||||
will_message_test(_Config) ->
|
||||
{ok, C1} = emqx_client:start_link([{clean_start, true},
|
||||
{will_topic, nth(3, ?TOPICS)},
|
||||
{will_payload, <<"client disconnected">>},
|
||||
{keepalive, 2}]),
|
||||
{ok, _} = emqx_client:connect(C1),
|
||||
|
||||
{ok, C2} = emqx_client:start_link(),
|
||||
{ok, _} = emqx_client:connect(C2),
|
||||
|
||||
{ok, _, [2]} = emqx_client:subscribe(C2, nth(3, ?TOPICS), 2),
|
||||
timer:sleep(10),
|
||||
ok = emqx_client:stop(C1),
|
||||
timer:sleep(5),
|
||||
?assertEqual(1, length(receive_messages(1))),
|
||||
ok = emqx_client:disconnect(C2),
|
||||
ct:print("Will message test succeeded").
|
||||
|
||||
offline_message_queueing_test(_) ->
|
||||
{ok, C1} = emqx_client:start_link([{clean_start, false},
|
||||
{client_id, <<"c1">>}]),
|
||||
{ok, _} = emqx_client:connect(C1),
|
||||
|
||||
{ok, _, [2]} = emqx_client:subscribe(C1, nth(6, ?WILD_TOPICS), 2),
|
||||
ok = emqx_client:disconnect(C1),
|
||||
{ok, C2} = emqx_client:start_link([{clean_start, true},
|
||||
{client_id, <<"c2">>}]),
|
||||
{ok, _} = emqx_client:connect(C2),
|
||||
|
||||
ok = emqx_client:publish(C2, nth(2, ?TOPICS), <<"qos 0">>, 0),
|
||||
{ok, _} = emqx_client:publish(C2, nth(3, ?TOPICS), <<"qos 1">>, 1),
|
||||
{ok, _} = emqx_client:publish(C2, nth(4, ?TOPICS), <<"qos 2">>, 2),
|
||||
timer:sleep(10),
|
||||
emqx_client:disconnect(C2),
|
||||
{ok, C3} = emqx_client:start_link([{clean_start, false},
|
||||
{client_id, <<"c1">>}]),
|
||||
{ok, _} = emqx_client:connect(C3),
|
||||
|
||||
timer:sleep(10),
|
||||
emqx_client:disconnect(C3),
|
||||
?assertEqual(3, length(receive_messages(3))).
|
||||
|
||||
overlapping_subscriptions_test(_) ->
|
||||
{ok, C} = emqx_client:start_link([]),
|
||||
{ok, _} = emqx_client:connect(C),
|
||||
|
||||
{ok, _, [2, 1]} = emqx_client:subscribe(C, [{nth(7, ?WILD_TOPICS), 2},
|
||||
{nth(1, ?WILD_TOPICS), 1}]),
|
||||
timer:sleep(10),
|
||||
{ok, _} = emqx_client:publish(C, nth(4, ?TOPICS), <<"overlapping topic filters">>, 2),
|
||||
timer:sleep(10),
|
||||
|
||||
Num = length(receive_messages(2)),
|
||||
?assert(lists:member(Num, [1, 2])),
|
||||
if
|
||||
Num == 1 ->
|
||||
ct:print("This server is publishing one message for all
|
||||
matching overlapping subscriptions, not one for each.");
|
||||
Num == 2 ->
|
||||
ct:print("This server is publishing one message per each
|
||||
matching overlapping subscription.");
|
||||
true -> ok
|
||||
end,
|
||||
emqx_client:disconnect(C).
|
||||
|
||||
%% keepalive_test(_) ->
|
||||
%% ct:print("Keepalive test starting"),
|
||||
%% {ok, C1, _} = emqx_client:start_link([{clean_start, true},
|
||||
%% {keepalive, 5},
|
||||
%% {will_flag, true},
|
||||
%% {will_topic, nth(5, ?TOPICS)},
|
||||
%% %% {will_qos, 2},
|
||||
%% {will_payload, <<"keepalive expiry">>}]),
|
||||
%% ok = emqx_client:pause(C1),
|
||||
%% {ok, C2, _} = emqx_client:start_link([{clean_start, true},
|
||||
%% {keepalive, 0}]),
|
||||
%% {ok, _, [2]} = emqx_client:subscribe(C2, nth(5, ?TOPICS), 2),
|
||||
%% ok = emqx_client:disconnect(C2),
|
||||
%% ?assertEqual(1, length(receive_messages(1))),
|
||||
%% ct:print("Keepalive test succeeded").
|
||||
|
||||
redelivery_on_reconnect_test(_) ->
|
||||
ct:print("Redelivery on reconnect test starting"),
|
||||
{ok, C1} = emqx_client:start_link([{clean_start, false},
|
||||
{client_id, <<"c">>}]),
|
||||
{ok, _} = emqx_client:connect(C1),
|
||||
|
||||
{ok, _, [2]} = emqx_client:subscribe(C1, nth(7, ?WILD_TOPICS), 2),
|
||||
timer:sleep(10),
|
||||
ok = emqx_client:pause(C1),
|
||||
{ok, _} = emqx_client:publish(C1, nth(2, ?TOPICS), <<>>,
|
||||
[{qos, 1}, {retain, false}]),
|
||||
{ok, _} = emqx_client:publish(C1, nth(4, ?TOPICS), <<>>,
|
||||
[{qos, 2}, {retain, false}]),
|
||||
timer:sleep(10),
|
||||
ok = emqx_client:disconnect(C1),
|
||||
?assertEqual(0, length(receive_messages(2))),
|
||||
{ok, C2} = emqx_client:start_link([{clean_start, false},
|
||||
{client_id, <<"c">>}]),
|
||||
{ok, _} = emqx_client:connect(C2),
|
||||
|
||||
timer:sleep(10),
|
||||
ok = emqx_client:disconnect(C2),
|
||||
?assertEqual(2, length(receive_messages(2))).
|
||||
|
||||
%% subscribe_failure_test(_) ->
|
||||
%% ct:print("Subscribe failure test starting"),
|
||||
%% {ok, C, _} = emqx_client:start_link([]),
|
||||
%% {ok, _, [2]} = emqx_client:subscribe(C, <<"$SYS/#">>, 2),
|
||||
%% timer:sleep(10),
|
||||
%% ct:print("Subscribe failure test succeeded").
|
||||
|
||||
dollar_topics_test(_) ->
|
||||
ct:print("$ topics test starting"),
|
||||
{ok, C} = emqx_client:start_link([{clean_start, true},
|
||||
{keepalive, 0}]),
|
||||
{ok, _} = emqx_client:connect(C),
|
||||
|
||||
{ok, _, [1]} = emqx_client:subscribe(C, nth(6, ?WILD_TOPICS), 1),
|
||||
{ok, _} = emqx_client:publish(C, << <<"$">>/binary, (nth(2, ?TOPICS))/binary>>,
|
||||
<<"test">>, [{qos, 1}, {retain, false}]),
|
||||
timer:sleep(10),
|
||||
?assertEqual(0, length(receive_messages(1))),
|
||||
ok = emqx_client:disconnect(C),
|
||||
ct:print("$ topics test succeeded").
|
|
@ -1,71 +0,0 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2019 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_cm_SUITE).
|
||||
|
||||
-compile(export_all).
|
||||
-compile(nowarn_export_all).
|
||||
|
||||
-include("emqx.hrl").
|
||||
-include("emqx_mqtt.hrl").
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
all() -> [{group, cm}].
|
||||
|
||||
groups() ->
|
||||
[{cm, [non_parallel_tests],
|
||||
[t_get_set_conn_attrs,
|
||||
t_get_set_conn_stats,
|
||||
t_lookup_conn_pid]}].
|
||||
|
||||
init_per_suite(Config) ->
|
||||
emqx_ct_helpers:start_apps([]),
|
||||
Config.
|
||||
|
||||
end_per_suite(_Config) ->
|
||||
emqx_ct_helpers:stop_apps([]).
|
||||
|
||||
init_per_testcase(_TestCase, Config) ->
|
||||
register_connection(),
|
||||
Config.
|
||||
|
||||
end_per_testcase(_TestCase, _Config) ->
|
||||
unregister_connection(),
|
||||
ok.
|
||||
|
||||
t_get_set_conn_attrs(_) ->
|
||||
?assert(emqx_cm:set_conn_attrs(<<"conn1">>, [{port, 8080}, {ip, "192.168.0.1"}])),
|
||||
?assert(emqx_cm:set_conn_attrs(<<"conn2">>, self(), [{port, 8080}, {ip, "192.168.0.2"}])),
|
||||
?assertEqual([{port, 8080}, {ip, "192.168.0.1"}], emqx_cm:get_conn_attrs(<<"conn1">>)),
|
||||
?assertEqual([{port, 8080}, {ip, "192.168.0.2"}], emqx_cm:get_conn_attrs(<<"conn2">>, self())).
|
||||
|
||||
t_get_set_conn_stats(_) ->
|
||||
?assert(emqx_cm:set_conn_stats(<<"conn1">>, [{count, 1}, {max, 2}])),
|
||||
?assert(emqx_cm:set_conn_stats(<<"conn2">>, self(), [{count, 1}, {max, 2}])),
|
||||
?assertEqual([{count, 1}, {max, 2}], emqx_cm:get_conn_stats(<<"conn1">>)),
|
||||
?assertEqual([{count, 1}, {max, 2}], emqx_cm:get_conn_stats(<<"conn2">>, self())).
|
||||
|
||||
t_lookup_conn_pid(_) ->
|
||||
?assertEqual(ok, emqx_cm:register_connection(<<"conn1">>, self())),
|
||||
?assertEqual(self(), emqx_cm:lookup_conn_pid(<<"conn1">>)).
|
||||
|
||||
register_connection() ->
|
||||
?assertEqual(ok, emqx_cm:register_connection(<<"conn1">>)),
|
||||
?assertEqual(ok, emqx_cm:register_connection(<<"conn2">>, self())).
|
||||
|
||||
unregister_connection() ->
|
||||
?assertEqual(ok, emqx_cm:unregister_connection(<<"conn1">>)),
|
||||
?assertEqual(ok, emqx_cm:unregister_connection(<<"conn2">>, self())).
|
|
@ -1,62 +0,0 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2019 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_flapping_SUITE).
|
||||
|
||||
-compile(export_all).
|
||||
-compile(nowarn_export_all).
|
||||
|
||||
-include("emqx.hrl").
|
||||
|
||||
-include_lib("common_test/include/ct.hrl").
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
all() ->
|
||||
[t_flapping].
|
||||
|
||||
init_per_suite(Config) ->
|
||||
emqx_ct_helpers:start_apps([]),
|
||||
prepare_for_test(),
|
||||
Config.
|
||||
|
||||
end_per_suite(_Config) ->
|
||||
emqx_ct_helpers:stop_apps([]).
|
||||
|
||||
t_flapping(_Config) ->
|
||||
process_flag(trap_exit, true),
|
||||
flapping_connect(5),
|
||||
{ok, C} = emqx_client:start_link([{client_id, <<"Client">>}]),
|
||||
{error, _} = emqx_client:connect(C),
|
||||
receive
|
||||
{'EXIT', Client, _Reason} ->
|
||||
ct:log("receive exit signal, Client: ~p", [Client])
|
||||
after 1000 ->
|
||||
ct:log("timeout")
|
||||
end.
|
||||
|
||||
|
||||
flapping_connect(Times) ->
|
||||
[flapping_connect() || _ <- lists:seq(1, Times)].
|
||||
|
||||
flapping_connect() ->
|
||||
{ok, C} = emqx_client:start_link([{client_id, <<"Client">>}]),
|
||||
{ok, _} = emqx_client:connect(C),
|
||||
ok = emqx_client:disconnect(C).
|
||||
|
||||
prepare_for_test() ->
|
||||
emqx_zone:set_env(external, enable_flapping_detect, true),
|
||||
emqx_zone:set_env(external, flapping_threshold, {10, 60}),
|
||||
emqx_zone:set_env(external, flapping_expiry_interval, 3600).
|
|
@ -37,60 +37,60 @@ all() ->
|
|||
|
||||
groups() ->
|
||||
[{connect, [parallel],
|
||||
[serialize_parse_connect,
|
||||
serialize_parse_v3_connect,
|
||||
serialize_parse_v4_connect,
|
||||
serialize_parse_v5_connect,
|
||||
serialize_parse_connect_without_clientid,
|
||||
serialize_parse_connect_with_will,
|
||||
serialize_parse_bridge_connect
|
||||
[t_serialize_parse_connect,
|
||||
t_serialize_parse_v3_connect,
|
||||
t_serialize_parse_v4_connect,
|
||||
t_serialize_parse_v5_connect,
|
||||
t_serialize_parse_connect_without_clientid,
|
||||
t_serialize_parse_connect_with_will,
|
||||
t_serialize_parse_bridge_connect
|
||||
]},
|
||||
{connack, [parallel],
|
||||
[serialize_parse_connack,
|
||||
serialize_parse_connack_v5
|
||||
[t_serialize_parse_connack,
|
||||
t_serialize_parse_connack_v5
|
||||
]},
|
||||
{publish, [parallel],
|
||||
[serialize_parse_qos0_publish,
|
||||
serialize_parse_qos1_publish,
|
||||
serialize_parse_qos2_publish,
|
||||
serialize_parse_publish_v5
|
||||
[t_serialize_parse_qos0_publish,
|
||||
t_serialize_parse_qos1_publish,
|
||||
t_serialize_parse_qos2_publish,
|
||||
t_serialize_parse_publish_v5
|
||||
]},
|
||||
{puback, [parallel],
|
||||
[serialize_parse_puback,
|
||||
serialize_parse_puback_v5,
|
||||
serialize_parse_pubrec,
|
||||
serialize_parse_pubrec_v5,
|
||||
serialize_parse_pubrel,
|
||||
serialize_parse_pubrel_v5,
|
||||
serialize_parse_pubcomp,
|
||||
serialize_parse_pubcomp_v5
|
||||
[t_serialize_parse_puback,
|
||||
t_serialize_parse_puback_v5,
|
||||
t_serialize_parse_pubrec,
|
||||
t_serialize_parse_pubrec_v5,
|
||||
t_serialize_parse_pubrel,
|
||||
t_serialize_parse_pubrel_v5,
|
||||
t_serialize_parse_pubcomp,
|
||||
t_serialize_parse_pubcomp_v5
|
||||
]},
|
||||
{subscribe, [parallel],
|
||||
[serialize_parse_subscribe,
|
||||
serialize_parse_subscribe_v5
|
||||
[t_serialize_parse_subscribe,
|
||||
t_serialize_parse_subscribe_v5
|
||||
]},
|
||||
{suback, [parallel],
|
||||
[serialize_parse_suback,
|
||||
serialize_parse_suback_v5
|
||||
[t_serialize_parse_suback,
|
||||
t_serialize_parse_suback_v5
|
||||
]},
|
||||
{unsubscribe, [parallel],
|
||||
[serialize_parse_unsubscribe,
|
||||
serialize_parse_unsubscribe_v5
|
||||
[t_serialize_parse_unsubscribe,
|
||||
t_serialize_parse_unsubscribe_v5
|
||||
]},
|
||||
{unsuback, [parallel],
|
||||
[serialize_parse_unsuback,
|
||||
serialize_parse_unsuback_v5
|
||||
[t_serialize_parse_unsuback,
|
||||
t_serialize_parse_unsuback_v5
|
||||
]},
|
||||
{ping, [parallel],
|
||||
[serialize_parse_pingreq,
|
||||
serialize_parse_pingresp
|
||||
[t_serialize_parse_pingreq,
|
||||
t_serialize_parse_pingresp
|
||||
]},
|
||||
{disconnect, [parallel],
|
||||
[serialize_parse_disconnect,
|
||||
serialize_parse_disconnect_v5
|
||||
[t_serialize_parse_disconnect,
|
||||
t_serialize_parse_disconnect_v5
|
||||
]},
|
||||
{auth, [parallel],
|
||||
[serialize_parse_auth_v5]
|
||||
[t_serialize_parse_auth_v5]
|
||||
}].
|
||||
|
||||
init_per_suite(Config) ->
|
||||
|
@ -105,7 +105,7 @@ init_per_group(_Group, Config) ->
|
|||
end_per_group(_Group, _Config) ->
|
||||
ok.
|
||||
|
||||
serialize_parse_connect(_) ->
|
||||
t_serialize_parse_connect(_) ->
|
||||
Packet1 = ?CONNECT_PACKET(#mqtt_packet_connect{}),
|
||||
?assertEqual(Packet1, parse_serialize(Packet1)),
|
||||
Packet2 = ?CONNECT_PACKET(#mqtt_packet_connect{
|
||||
|
@ -119,7 +119,7 @@ serialize_parse_connect(_) ->
|
|||
}),
|
||||
?assertEqual(Packet2, parse_serialize(Packet2)).
|
||||
|
||||
serialize_parse_v3_connect(_) ->
|
||||
t_serialize_parse_v3_connect(_) ->
|
||||
Bin = <<16,37,0,6,77,81,73,115,100,112,3,2,0,60,0,23,109,111,115,
|
||||
113,112,117, 98,47,49,48,52,53,49,45,105,77,97,99,46,108,
|
||||
111,99,97>>,
|
||||
|
@ -132,7 +132,7 @@ serialize_parse_v3_connect(_) ->
|
|||
}),
|
||||
?assertMatch({ok, Packet, <<>>, _}, emqx_frame:parse(Bin)).
|
||||
|
||||
serialize_parse_v4_connect(_) ->
|
||||
t_serialize_parse_v4_connect(_) ->
|
||||
Bin = <<16,35,0,4,77,81,84,84,4,2,0,60,0,23,109,111,115,113,112,117,
|
||||
98,47,49,48,52,53,49,45,105,77,97,99,46,108,111,99,97>>,
|
||||
Packet = ?CONNECT_PACKET(#mqtt_packet_connect{proto_ver = 4,
|
||||
|
@ -143,7 +143,7 @@ serialize_parse_v4_connect(_) ->
|
|||
?assertEqual(Bin, serialize_to_binary(Packet)),
|
||||
?assertMatch({ok, Packet, <<>>, _}, emqx_frame:parse(Bin)).
|
||||
|
||||
serialize_parse_v5_connect(_) ->
|
||||
t_serialize_parse_v5_connect(_) ->
|
||||
Props = #{'Session-Expiry-Interval' => 60,
|
||||
'Receive-Maximum' => 100,
|
||||
'Maximum-QoS' => ?QOS_2,
|
||||
|
@ -183,7 +183,7 @@ serialize_parse_v5_connect(_) ->
|
|||
}),
|
||||
?assertEqual(Packet, parse_serialize(Packet)).
|
||||
|
||||
serialize_parse_connect_without_clientid(_) ->
|
||||
t_serialize_parse_connect_without_clientid(_) ->
|
||||
Bin = <<16,12,0,4,77,81,84,84,4,2,0,60,0,0>>,
|
||||
Packet = ?CONNECT_PACKET(
|
||||
#mqtt_packet_connect{proto_ver = 4,
|
||||
|
@ -195,7 +195,7 @@ serialize_parse_connect_without_clientid(_) ->
|
|||
?assertEqual(Bin, serialize_to_binary(Packet)),
|
||||
?assertMatch({ok, Packet, <<>>, _}, emqx_frame:parse(Bin)).
|
||||
|
||||
serialize_parse_connect_with_will(_) ->
|
||||
t_serialize_parse_connect_with_will(_) ->
|
||||
Bin = <<16,67,0,6,77,81,73,115,100,112,3,206,0,60,0,23,109,111,115,113,112,
|
||||
117,98,47,49,48,52,53,50,45,105,77,97,99,46,108,111,99,97,0,5,47,119,
|
||||
105,108,108,0,7,119,105,108,108,109,115,103,0,4,116,101,115,116,0,6,
|
||||
|
@ -217,7 +217,7 @@ serialize_parse_connect_with_will(_) ->
|
|||
?assertEqual(Bin, serialize_to_binary(Packet)),
|
||||
?assertMatch({ok, Packet, <<>>, _}, emqx_frame:parse(Bin)).
|
||||
|
||||
serialize_parse_bridge_connect(_) ->
|
||||
t_serialize_parse_bridge_connect(_) ->
|
||||
Bin = <<16,86,0,6,77,81,73,115,100,112,131,44,0,60,0,19,67,95,48,48,58,48,67,
|
||||
58,50,57,58,50,66,58,55,55,58,53,50,0,48,36,83,89,83,47,98,114,111,107,
|
||||
101,114,47,99,111,110,110,101,99,116,105,111,110,47,67,95,48,48,58,48,
|
||||
|
@ -239,12 +239,12 @@ serialize_parse_bridge_connect(_) ->
|
|||
?assertEqual(Bin, serialize_to_binary(Packet)),
|
||||
?assertMatch({ok, Packet, <<>>, _}, emqx_frame:parse(Bin)).
|
||||
|
||||
serialize_parse_connack(_) ->
|
||||
t_serialize_parse_connack(_) ->
|
||||
Packet = ?CONNACK_PACKET(?RC_SUCCESS),
|
||||
?assertEqual(<<32,2,0,0>>, serialize_to_binary(Packet)),
|
||||
?assertEqual(Packet, parse_serialize(Packet)).
|
||||
|
||||
serialize_parse_connack_v5(_) ->
|
||||
t_serialize_parse_connack_v5(_) ->
|
||||
Props = #{'Session-Expiry-Interval' => 60,
|
||||
'Receive-Maximum' => 100,
|
||||
'Maximum-QoS' => ?QOS_2,
|
||||
|
@ -265,7 +265,7 @@ serialize_parse_connack_v5(_) ->
|
|||
Packet = ?CONNACK_PACKET(?RC_SUCCESS, 0, Props),
|
||||
?assertEqual(Packet, parse_serialize(Packet, #{version => ?MQTT_PROTO_V5})).
|
||||
|
||||
serialize_parse_qos0_publish(_) ->
|
||||
t_serialize_parse_qos0_publish(_) ->
|
||||
Bin = <<48,14,0,7,120,120,120,47,121,121,121,104,101,108,108,111>>,
|
||||
Packet = #mqtt_packet{header = #mqtt_packet_header{type = ?PUBLISH,
|
||||
dup = false,
|
||||
|
@ -277,7 +277,7 @@ serialize_parse_qos0_publish(_) ->
|
|||
?assertEqual(Bin, serialize_to_binary(Packet)),
|
||||
?assertMatch({ok, Packet, <<>>, _}, emqx_frame:parse(Bin)).
|
||||
|
||||
serialize_parse_qos1_publish(_) ->
|
||||
t_serialize_parse_qos1_publish(_) ->
|
||||
Bin = <<50,13,0,5,97,47,98,47,99,0,1,104,97,104,97>>,
|
||||
Packet = #mqtt_packet{header = #mqtt_packet_header{type = ?PUBLISH,
|
||||
dup = false,
|
||||
|
@ -289,11 +289,11 @@ serialize_parse_qos1_publish(_) ->
|
|||
?assertEqual(Bin, serialize_to_binary(Packet)),
|
||||
?assertMatch({ok, Packet, <<>>, _}, emqx_frame:parse(Bin)).
|
||||
|
||||
serialize_parse_qos2_publish(_) ->
|
||||
t_serialize_parse_qos2_publish(_) ->
|
||||
Packet = ?PUBLISH_PACKET(?QOS_2, <<"Topic">>, 1, payload()),
|
||||
?assertEqual(Packet, parse_serialize(Packet)).
|
||||
|
||||
serialize_parse_publish_v5(_) ->
|
||||
t_serialize_parse_publish_v5(_) ->
|
||||
Props = #{'Payload-Format-Indicator' => 1,
|
||||
'Message-Expiry-Interval' => 60,
|
||||
'Topic-Alias' => 16#AB,
|
||||
|
@ -304,45 +304,45 @@ serialize_parse_publish_v5(_) ->
|
|||
Packet = ?PUBLISH_PACKET(?QOS_1, <<"$share/group/topic">>, 1, Props, <<"payload">>),
|
||||
?assertEqual(Packet, parse_serialize(Packet, #{version => ?MQTT_PROTO_V5})).
|
||||
|
||||
serialize_parse_puback(_) ->
|
||||
t_serialize_parse_puback(_) ->
|
||||
Packet = ?PUBACK_PACKET(1),
|
||||
?assertEqual(<<64,2,0,1>>, serialize_to_binary(Packet)),
|
||||
?assertEqual(Packet, parse_serialize(Packet)).
|
||||
|
||||
serialize_parse_puback_v5(_) ->
|
||||
t_serialize_parse_puback_v5(_) ->
|
||||
Packet = ?PUBACK_PACKET(16, ?RC_SUCCESS, #{'Reason-String' => <<"success">>}),
|
||||
?assertEqual(Packet, parse_serialize(Packet, #{version => ?MQTT_PROTO_V5})).
|
||||
|
||||
serialize_parse_pubrec(_) ->
|
||||
t_serialize_parse_pubrec(_) ->
|
||||
Packet = ?PUBREC_PACKET(1),
|
||||
?assertEqual(<<5:4,0:4,2,0,1>>, serialize_to_binary(Packet)),
|
||||
?assertEqual(Packet, parse_serialize(Packet)).
|
||||
|
||||
serialize_parse_pubrec_v5(_) ->
|
||||
t_serialize_parse_pubrec_v5(_) ->
|
||||
Packet = ?PUBREC_PACKET(16, ?RC_SUCCESS, #{'Reason-String' => <<"success">>}),
|
||||
?assertEqual(Packet, parse_serialize(Packet, #{version => ?MQTT_PROTO_V5})).
|
||||
|
||||
serialize_parse_pubrel(_) ->
|
||||
t_serialize_parse_pubrel(_) ->
|
||||
Packet = ?PUBREL_PACKET(1),
|
||||
Bin = serialize_to_binary(Packet),
|
||||
?assertEqual(<<6:4,2:4,2,0,1>>, Bin),
|
||||
?assertEqual(Packet, parse_serialize(Packet)).
|
||||
|
||||
serialize_parse_pubrel_v5(_) ->
|
||||
t_serialize_parse_pubrel_v5(_) ->
|
||||
Packet = ?PUBREL_PACKET(16, ?RC_SUCCESS, #{'Reason-String' => <<"success">>}),
|
||||
?assertEqual(Packet, parse_serialize(Packet, #{version => ?MQTT_PROTO_V5})).
|
||||
|
||||
serialize_parse_pubcomp(_) ->
|
||||
t_serialize_parse_pubcomp(_) ->
|
||||
Packet = ?PUBCOMP_PACKET(1),
|
||||
Bin = serialize_to_binary(Packet),
|
||||
?assertEqual(<<7:4,0:4,2,0,1>>, Bin),
|
||||
?assertEqual(Packet, parse_serialize(Packet)).
|
||||
|
||||
serialize_parse_pubcomp_v5(_) ->
|
||||
t_serialize_parse_pubcomp_v5(_) ->
|
||||
Packet = ?PUBCOMP_PACKET(16, ?RC_SUCCESS, #{'Reason-String' => <<"success">>}),
|
||||
?assertEqual(Packet, parse_serialize(Packet, #{version => ?MQTT_PROTO_V5})).
|
||||
|
||||
serialize_parse_subscribe(_) ->
|
||||
t_serialize_parse_subscribe(_) ->
|
||||
%% SUBSCRIBE(Q1, R0, D0, PacketId=2, TopicTable=[{<<"TopicA">>,2}])
|
||||
Bin = <<130,11,0,2,0,6,84,111,112,105,99,65,2>>,
|
||||
TopicOpts = #{nl => 0 , rap => 0, rc => 0, rh => 0, qos => 2},
|
||||
|
@ -352,61 +352,61 @@ serialize_parse_subscribe(_) ->
|
|||
%%ct:log("Bin: ~p, Packet: ~p ~n", [Packet, parse(Bin)]),
|
||||
?assertMatch({ok, Packet, <<>>, _}, emqx_frame:parse(Bin)).
|
||||
|
||||
serialize_parse_subscribe_v5(_) ->
|
||||
t_serialize_parse_subscribe_v5(_) ->
|
||||
TopicFilters = [{<<"TopicQos0">>, #{rh => 1, qos => ?QOS_2, rap => 0, nl => 0, rc => 0}},
|
||||
{<<"TopicQos1">>, #{rh => 1, qos => ?QOS_2, rap => 0, nl => 0, rc => 0}}],
|
||||
Packet = ?SUBSCRIBE_PACKET(3, #{'Subscription-Identifier' => 16#FFFFFFF}, TopicFilters),
|
||||
?assertEqual(Packet, parse_serialize(Packet, #{version => ?MQTT_PROTO_V5})).
|
||||
|
||||
serialize_parse_suback(_) ->
|
||||
t_serialize_parse_suback(_) ->
|
||||
Packet = ?SUBACK_PACKET(10, [?QOS_0, ?QOS_1, 128]),
|
||||
?assertEqual(Packet, parse_serialize(Packet)).
|
||||
|
||||
serialize_parse_suback_v5(_) ->
|
||||
t_serialize_parse_suback_v5(_) ->
|
||||
Packet = ?SUBACK_PACKET(1, #{'Reason-String' => <<"success">>,
|
||||
'User-Property' => [{<<"key">>, <<"value">>}]},
|
||||
[?QOS_0, ?QOS_1, 128]),
|
||||
?assertEqual(Packet, parse_serialize(Packet, #{version => ?MQTT_PROTO_V5})).
|
||||
|
||||
serialize_parse_unsubscribe(_) ->
|
||||
t_serialize_parse_unsubscribe(_) ->
|
||||
%% UNSUBSCRIBE(Q1, R0, D0, PacketId=2, TopicTable=[<<"TopicA">>])
|
||||
Packet = ?UNSUBSCRIBE_PACKET(2, [<<"TopicA">>]),
|
||||
Bin = <<162,10,0,2,0,6,84,111,112,105,99,65>>,
|
||||
?assertEqual(Bin, serialize_to_binary(Packet)),
|
||||
?assertMatch({ok, Packet, <<>>, _}, emqx_frame:parse(Bin)).
|
||||
|
||||
serialize_parse_unsubscribe_v5(_) ->
|
||||
t_serialize_parse_unsubscribe_v5(_) ->
|
||||
Props = #{'User-Property' => [{<<"key">>, <<"val">>}]},
|
||||
Packet = ?UNSUBSCRIBE_PACKET(10, Props, [<<"Topic1">>, <<"Topic2">>]),
|
||||
?assertEqual(Packet, parse_serialize(Packet, #{version => ?MQTT_PROTO_V5})).
|
||||
|
||||
serialize_parse_unsuback(_) ->
|
||||
t_serialize_parse_unsuback(_) ->
|
||||
Packet = ?UNSUBACK_PACKET(10),
|
||||
?assertEqual(Packet, parse_serialize(Packet)).
|
||||
|
||||
serialize_parse_unsuback_v5(_) ->
|
||||
t_serialize_parse_unsuback_v5(_) ->
|
||||
Packet = ?UNSUBACK_PACKET(10, #{'Reason-String' => <<"Not authorized">>,
|
||||
'User-Property' => [{<<"key">>, <<"val">>}]},
|
||||
[16#87, 16#87, 16#87]),
|
||||
?assertEqual(Packet, parse_serialize(Packet, #{version => ?MQTT_PROTO_V5})).
|
||||
|
||||
serialize_parse_pingreq(_) ->
|
||||
t_serialize_parse_pingreq(_) ->
|
||||
PingReq = ?PACKET(?PINGREQ),
|
||||
?assertEqual(PingReq, parse_serialize(PingReq)).
|
||||
|
||||
serialize_parse_pingresp(_) ->
|
||||
t_serialize_parse_pingresp(_) ->
|
||||
PingResp = ?PACKET(?PINGRESP),
|
||||
?assertEqual(PingResp, parse_serialize(PingResp)).
|
||||
|
||||
parse_disconnect(_) ->
|
||||
t_parse_disconnect(_) ->
|
||||
Packet = ?DISCONNECT_PACKET(?RC_SUCCESS),
|
||||
?assertMatch({ok, Packet, <<>>, _}, emqx_frame:parse(<<224, 0>>)).
|
||||
|
||||
serialize_parse_disconnect(_) ->
|
||||
t_serialize_parse_disconnect(_) ->
|
||||
Packet = ?DISCONNECT_PACKET(?RC_SUCCESS),
|
||||
?assertEqual(Packet, parse_serialize(Packet)).
|
||||
|
||||
serialize_parse_disconnect_v5(_) ->
|
||||
t_serialize_parse_disconnect_v5(_) ->
|
||||
Packet = ?DISCONNECT_PACKET(?RC_SUCCESS,
|
||||
#{'Session-Expiry-Interval' => 60,
|
||||
'Reason-String' => <<"server_moved">>,
|
||||
|
@ -414,7 +414,7 @@ serialize_parse_disconnect_v5(_) ->
|
|||
}),
|
||||
?assertEqual(Packet, parse_serialize(Packet, #{version => ?MQTT_PROTO_V5})).
|
||||
|
||||
serialize_parse_auth_v5(_) ->
|
||||
t_serialize_parse_auth_v5(_) ->
|
||||
Packet = ?AUTH_PACKET(?RC_SUCCESS,
|
||||
#{'Authentication-Method' => <<"oauth2">>,
|
||||
'Authentication-Data' => <<"3zekkd">>,
|
||||
|
@ -427,7 +427,8 @@ parse_serialize(Packet) ->
|
|||
parse_serialize(Packet, #{}).
|
||||
|
||||
parse_serialize(Packet, Opts) when is_map(Opts) ->
|
||||
Bin = iolist_to_binary(emqx_frame:serialize(Packet, Opts)),
|
||||
Ver = maps:get(version, Opts, ?MQTT_PROTO_V4),
|
||||
Bin = iolist_to_binary(emqx_frame:serialize(Packet, Ver)),
|
||||
ParseState = emqx_frame:initial_parse_state(Opts),
|
||||
{ok, NPacket, <<>>, _} = emqx_frame:parse(Bin, ParseState),
|
||||
NPacket.
|
||||
|
|
|
@ -21,8 +21,7 @@
|
|||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
all() ->
|
||||
[t_init, t_run, t_info, t_reset].
|
||||
all() -> emqx_ct:all(?MODULE).
|
||||
|
||||
t_init(_) ->
|
||||
?assertEqual(undefined, emqx_gc:init(false)),
|
||||
|
|
|
@ -16,21 +16,21 @@
|
|||
|
||||
-module(emqx_guid_SUITE).
|
||||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
-compile(export_all).
|
||||
-compile(nowarn_export_all).
|
||||
|
||||
all() -> [t_guid_gen, t_guid_hexstr, t_guid_base62].
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
all() -> emqx_ct:all(?MODULE).
|
||||
|
||||
t_guid_gen(_) ->
|
||||
Guid1 = emqx_guid:gen(),
|
||||
Guid2 = emqx_guid:gen(),
|
||||
<<_:128>> = Guid1,
|
||||
true = (Guid2 >= Guid1),
|
||||
?assert((Guid2 >= Guid1)),
|
||||
{Ts1, _, 0} = emqx_guid:new(),
|
||||
Ts2 = emqx_guid:timestamp(emqx_guid:gen()),
|
||||
true = Ts2 > Ts1.
|
||||
?assert(Ts2 > Ts1).
|
||||
|
||||
t_guid_hexstr(_) ->
|
||||
Guid = emqx_guid:gen(),
|
||||
|
|
|
@ -20,12 +20,10 @@
|
|||
-compile(nowarn_export_all).
|
||||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
-include_lib("common_test/include/ct.hrl").
|
||||
|
||||
all() ->
|
||||
[add_delete_hook, run_hook].
|
||||
all() -> emqx_ct:all(?MODULE).
|
||||
|
||||
add_delete_hook(_) ->
|
||||
t_add_del_hook(_) ->
|
||||
{ok, _} = emqx_hooks:start_link(),
|
||||
ok = emqx:hook(test_hook, fun ?MODULE:hook_fun1/1, []),
|
||||
ok = emqx:hook(test_hook, fun ?MODULE:hook_fun2/1, []),
|
||||
|
@ -56,7 +54,7 @@ add_delete_hook(_) ->
|
|||
?assertEqual([], emqx_hooks:lookup(emqx_hook)),
|
||||
ok = emqx_hooks:stop().
|
||||
|
||||
run_hook(_) ->
|
||||
t_run_hooks(_) ->
|
||||
{ok, _} = emqx_hooks:start_link(),
|
||||
ok = emqx:hook(foldl_hook, fun ?MODULE:hook_fun3/4, [init]),
|
||||
ok = emqx:hook(foldl_hook, {?MODULE, hook_fun3, [init]}),
|
||||
|
@ -86,6 +84,10 @@ run_hook(_) ->
|
|||
|
||||
ok = emqx_hooks:stop().
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Hook fun
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
hook_fun1(arg) -> ok;
|
||||
hook_fun1(_) -> error.
|
||||
|
||||
|
@ -104,7 +106,7 @@ hook_fun7(arg, initArg) -> ok.
|
|||
hook_fun8(arg, initArg) -> ok.
|
||||
|
||||
hook_fun9(arg, Acc) -> {stop, [r9 | Acc]}.
|
||||
hook_fun10(arg, Acc) -> {stop, [r10 | Acc]}.
|
||||
hook_fun10(arg, Acc) -> {stop, [r10 | Acc]}.
|
||||
|
||||
hook_filter1(arg) -> true;
|
||||
hook_filter1(_) -> false.
|
||||
|
@ -112,6 +114,7 @@ hook_filter1(_) -> false.
|
|||
hook_filter2(arg, _Acc, init_arg) -> true;
|
||||
hook_filter2(_, _Acc, _IntArg) -> false.
|
||||
|
||||
hook_filter2_1(arg, _Acc, init_arg) -> true;
|
||||
hook_filter2_1(arg, _Acc, init_arg) -> true;
|
||||
hook_filter2_1(arg1, _Acc, init_arg) -> true;
|
||||
hook_filter2_1(_, _Acc, _IntArg) -> false.
|
||||
hook_filter2_1(_, _Acc, _IntArg) -> false.
|
||||
|
||||
|
|
|
@ -19,26 +19,73 @@
|
|||
-compile(export_all).
|
||||
-compile(nowarn_export_all).
|
||||
|
||||
all() -> [t_inflight_all].
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
-include_lib("emqx_ct_helpers/include/emqx_ct.hrl").
|
||||
|
||||
t_inflight_all(_) ->
|
||||
Empty = emqx_inflight:new(2),
|
||||
true = emqx_inflight:is_empty(Empty),
|
||||
2 = emqx_inflight:max_size(Empty),
|
||||
false = emqx_inflight:contain(a, Empty),
|
||||
none = emqx_inflight:lookup(a, Empty),
|
||||
try emqx_inflight:update(a, 1, Empty) catch
|
||||
error:Reason -> io:format("Reason: ~w~n", [Reason])
|
||||
end,
|
||||
0 = emqx_inflight:size(Empty),
|
||||
Inflight1 = emqx_inflight:insert(a, 1, Empty),
|
||||
Inflight2 = emqx_inflight:insert(b, 2, Inflight1),
|
||||
2 = emqx_inflight:size(Inflight2),
|
||||
true = emqx_inflight:is_full(Inflight2),
|
||||
{value, 1} = emqx_inflight:lookup(a, Inflight1),
|
||||
{value, 2} = emqx_inflight:lookup(a, emqx_inflight:update(a, 2, Inflight1)),
|
||||
false = emqx_inflight:contain(a, emqx_inflight:delete(a, Inflight1)),
|
||||
[1, 2] = emqx_inflight:values(Inflight2),
|
||||
[{a, 1}, {b ,2}] = emqx_inflight:to_list(Inflight2),
|
||||
[a, b] = emqx_inflight:window(Inflight2).
|
||||
all() -> emqx_ct:all(?MODULE).
|
||||
|
||||
t_contain(_) ->
|
||||
Inflight = emqx_inflight:insert(k, v, emqx_inflight:new()),
|
||||
?assert(emqx_inflight:contain(k, Inflight)),
|
||||
?assertNot(emqx_inflight:contain(badkey, Inflight)).
|
||||
|
||||
t_lookup(_) ->
|
||||
Inflight = emqx_inflight:insert(k, v, emqx_inflight:new()),
|
||||
?assertEqual({value, v}, emqx_inflight:lookup(k, Inflight)),
|
||||
?assertEqual(none, emqx_inflight:lookup(badkey, Inflight)).
|
||||
|
||||
t_insert(_) ->
|
||||
Inflight = emqx_inflight:insert(
|
||||
b, 2, emqx_inflight:insert(
|
||||
a, 1, emqx_inflight:new())),
|
||||
?assertEqual(2, emqx_inflight:size(Inflight)),
|
||||
?assertEqual({value, 1}, emqx_inflight:lookup(a, Inflight)),
|
||||
?assertEqual({value, 2}, emqx_inflight:lookup(b, Inflight)),
|
||||
?catch_error({key_exists, a}, emqx_inflight:insert(a, 1, Inflight)).
|
||||
|
||||
t_update(_) ->
|
||||
Inflight = emqx_inflight:insert(k, v, emqx_inflight:new()),
|
||||
?assertEqual(Inflight, emqx_inflight:update(k, v, Inflight)),
|
||||
?catch_error(function_clause, emqx_inflight:update(badkey, v, Inflight)).
|
||||
|
||||
t_resize(_) ->
|
||||
Inflight = emqx_inflight:insert(k, v, emqx_inflight:new(2)),
|
||||
?assertEqual(1, emqx_inflight:size(Inflight)),
|
||||
?assertEqual(2, emqx_inflight:max_size(Inflight)),
|
||||
Inflight1 = emqx_inflight:resize(4, Inflight),
|
||||
?assertEqual(4, emqx_inflight:max_size(Inflight1)),
|
||||
?assertEqual(1, emqx_inflight:size(Inflight)).
|
||||
|
||||
t_delete(_) ->
|
||||
Inflight = emqx_inflight:insert(k, v, emqx_inflight:new(2)),
|
||||
Inflight1 = emqx_inflight:delete(k, Inflight),
|
||||
?assert(emqx_inflight:is_empty(Inflight1)),
|
||||
?assertNot(emqx_inflight:contain(k, Inflight1)).
|
||||
|
||||
t_values(_) ->
|
||||
Inflight = emqx_inflight:insert(
|
||||
b, 2, emqx_inflight:insert(
|
||||
a, 1, emqx_inflight:new())),
|
||||
?assertEqual([1,2], emqx_inflight:values(Inflight)),
|
||||
?assertEqual([{a,1},{b,2}], emqx_inflight:to_list(Inflight)).
|
||||
|
||||
t_is_full(_) ->
|
||||
Inflight = emqx_inflight:insert(k, v, emqx_inflight:new()),
|
||||
?assertNot(emqx_inflight:is_full(Inflight)),
|
||||
Inflight1 = emqx_inflight:insert(
|
||||
b, 2, emqx_inflight:insert(
|
||||
a, 1, emqx_inflight:new(2))),
|
||||
?assert(emqx_inflight:is_full(Inflight1)).
|
||||
|
||||
t_is_empty(_) ->
|
||||
Inflight = emqx_inflight:insert(a, 1, emqx_inflight:new(2)),
|
||||
?assertNot(emqx_inflight:is_empty(Inflight)),
|
||||
Inflight1 = emqx_inflight:delete(a, Inflight),
|
||||
?assert(emqx_inflight:is_empty(Inflight1)).
|
||||
|
||||
t_window(_) ->
|
||||
Inflight = emqx_inflight:insert(
|
||||
b, 2, emqx_inflight:insert(
|
||||
a, 1, emqx_inflight:new(2))),
|
||||
[a, b] = emqx_inflight:window(Inflight).
|
||||
|
||||
|
|
|
@ -19,21 +19,26 @@
|
|||
-compile(export_all).
|
||||
-compile(nowarn_export_all).
|
||||
|
||||
all() -> [t_decode_encode, t_safe_decode_encode].
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
-define(DEC_OPTS, [{labels, atom}, return_maps]).
|
||||
|
||||
all() -> emqx_ct:all(?MODULE).
|
||||
|
||||
t_decode_encode(_) ->
|
||||
JsonText = <<"{\"library\": \"jsx\", \"awesome\": true}">>,
|
||||
JsonTerm = emqx_json:decode(JsonText),
|
||||
JsonMaps = #{library => <<"jsx">>, awesome => true},
|
||||
JsonMaps = emqx_json:decode(JsonText, [{labels, atom}, return_maps]),
|
||||
JsonText = emqx_json:encode(JsonTerm, [{space, 1}]).
|
||||
?assertEqual(JsonText, emqx_json:encode(JsonTerm, [{space, 1}])),
|
||||
?assertEqual(JsonMaps, emqx_json:decode(JsonText, ?DEC_OPTS)).
|
||||
|
||||
t_safe_decode_encode(_) ->
|
||||
JsonText = <<"{\"library\": \"jsx\", \"awesome\": true}">>,
|
||||
{ok, JsonTerm} = emqx_json:safe_decode(JsonText),
|
||||
JsonMaps = #{library => <<"jsx">>, awesome => true},
|
||||
{ok, JsonMaps} = emqx_json:safe_decode(JsonText, [{labels, atom}, return_maps]),
|
||||
{ok, JsonText} = emqx_json:safe_encode(JsonTerm, [{space, 1}]),
|
||||
?assertEqual({ok, JsonText}, emqx_json:safe_encode(JsonTerm, [{space, 1}])),
|
||||
?assertEqual({ok, JsonMaps}, emqx_json:safe_decode(JsonText, ?DEC_OPTS)),
|
||||
BadJsonText = <<"{\"library\", \"awesome\": true}">>,
|
||||
{error, _} = emqx_json:safe_decode(BadJsonText),
|
||||
{error, _} = emqx_json:safe_encode({a, {b ,1}}).
|
||||
?assertEqual({error, badarg}, emqx_json:safe_decode(BadJsonText)),
|
||||
{error, badarg} = emqx_json:safe_encode({a, {b ,1}}).
|
||||
|
||||
|
|
|
@ -19,13 +19,7 @@
|
|||
-compile(export_all).
|
||||
-compile(nowarn_export_all).
|
||||
|
||||
all() -> [{group, keepalive}].
|
||||
|
||||
groups() -> [{keepalive, [], [t_keepalive]}].
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Keepalive
|
||||
%%--------------------------------------------------------------------
|
||||
all() -> emqx_ct:all(?MODULE).
|
||||
|
||||
t_keepalive(_) ->
|
||||
{ok, KA} = emqx_keepalive:start(fun() -> {ok, 1} end, 1, {keepalive, timeout}),
|
||||
|
@ -38,7 +32,6 @@ keepalive_recv(KA, Acc) ->
|
|||
{ok, KA1} -> keepalive_recv(KA1, [resumed | Acc]);
|
||||
{error, timeout} -> [timeout | Acc]
|
||||
end
|
||||
after 4000 ->
|
||||
Acc
|
||||
after 4000 -> Acc
|
||||
end.
|
||||
|
||||
|
|
|
@ -1,175 +0,0 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2019 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_lib_SUITE).
|
||||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
-compile(export_all).
|
||||
-compile(nowarn_export_all).
|
||||
|
||||
-define(SOCKOPTS, [
|
||||
binary,
|
||||
{packet, raw},
|
||||
{reuseaddr, true},
|
||||
{backlog, 512},
|
||||
{nodelay, true}
|
||||
]).
|
||||
|
||||
-define(PQ, emqx_pqueue).
|
||||
|
||||
-define(BASE62, emqx_base62).
|
||||
|
||||
all() -> [{group, guid}, {group, opts},
|
||||
{group, ?PQ}, {group, time},
|
||||
{group, node}, {group, base62}].
|
||||
|
||||
groups() ->
|
||||
[{guid, [], [guid_gen, guid_hexstr, guid_base62]},
|
||||
{opts, [], [opts_merge]},
|
||||
{?PQ, [], [priority_queue_plen,
|
||||
priority_queue_out2]},
|
||||
{time, [], [time_now_to_]},
|
||||
{node, [], [node_is_aliving, node_parse_name]},
|
||||
{base62, [], [base62_encode]}].
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% emqx_guid
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
guid_gen(_) ->
|
||||
Guid1 = emqx_guid:gen(),
|
||||
Guid2 = emqx_guid:gen(),
|
||||
<<_:128>> = Guid1,
|
||||
true = (Guid2 >= Guid1),
|
||||
{Ts1, _, 0} = emqx_guid:new(),
|
||||
Ts2 = emqx_guid:timestamp(emqx_guid:gen()),
|
||||
true = Ts2 > Ts1.
|
||||
|
||||
guid_hexstr(_) ->
|
||||
Guid = emqx_guid:gen(),
|
||||
?assertEqual(Guid, emqx_guid:from_hexstr(emqx_guid:to_hexstr(Guid))).
|
||||
|
||||
guid_base62(_) ->
|
||||
Guid = emqx_guid:gen(),
|
||||
?assertEqual(Guid, emqx_guid:from_base62(emqx_guid:to_base62(Guid))).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% emqx_opts
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
opts_merge(_) ->
|
||||
Opts = emqx_misc:merge_opts(?SOCKOPTS, [raw,
|
||||
binary,
|
||||
{backlog, 1024},
|
||||
{nodelay, false},
|
||||
{max_clients, 1024},
|
||||
{acceptors, 16}]),
|
||||
1024 = proplists:get_value(backlog, Opts),
|
||||
1024 = proplists:get_value(max_clients, Opts),
|
||||
[binary, raw,
|
||||
{acceptors, 16},
|
||||
{backlog, 1024},
|
||||
{max_clients, 1024},
|
||||
{nodelay, false},
|
||||
{packet, raw},
|
||||
{reuseaddr, true}] = lists:sort(Opts).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% priority_queue
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
priority_queue_plen(_) ->
|
||||
Q = ?PQ:new(),
|
||||
0 = ?PQ:plen(0, Q),
|
||||
Q0 = ?PQ:in(z, Q),
|
||||
1 = ?PQ:plen(0, Q0),
|
||||
Q1 = ?PQ:in(x, 1, Q0),
|
||||
1 = ?PQ:plen(1, Q1),
|
||||
Q2 = ?PQ:in(y, 2, Q1),
|
||||
1 = ?PQ:plen(2, Q2),
|
||||
Q3 = ?PQ:in(z, 2, Q2),
|
||||
2 = ?PQ:plen(2, Q3),
|
||||
{_, Q4} = ?PQ:out(1, Q3),
|
||||
0 = ?PQ:plen(1, Q4),
|
||||
{_, Q5} = ?PQ:out(Q4),
|
||||
1 = ?PQ:plen(2, Q5),
|
||||
{_, Q6} = ?PQ:out(Q5),
|
||||
0 = ?PQ:plen(2, Q6),
|
||||
1 = ?PQ:len(Q6),
|
||||
{_, Q7} = ?PQ:out(Q6),
|
||||
0 = ?PQ:len(Q7).
|
||||
|
||||
priority_queue_out2(_) ->
|
||||
Els = [a, {b, 1}, {c, 1}, {d, 2}, {e, 2}, {f, 2}],
|
||||
Q = ?PQ:new(),
|
||||
Q0 = lists:foldl(
|
||||
fun({El, P}, Acc) ->
|
||||
?PQ:in(El, P, Acc);
|
||||
(El, Acc) ->
|
||||
?PQ:in(El, Acc)
|
||||
end, Q, Els),
|
||||
{Val, Q1} = ?PQ:out(Q0),
|
||||
{value, d} = Val,
|
||||
{Val1, Q2} = ?PQ:out(2, Q1),
|
||||
{value, e} = Val1,
|
||||
{Val2, Q3} = ?PQ:out(1, Q2),
|
||||
{value, b} = Val2,
|
||||
{Val3, Q4} = ?PQ:out(Q3),
|
||||
{value, f} = Val3,
|
||||
{Val4, Q5} = ?PQ:out(Q4),
|
||||
{value, c} = Val4,
|
||||
{Val5, Q6} = ?PQ:out(Q5),
|
||||
{value, a} = Val5,
|
||||
{empty, _Q7} = ?PQ:out(Q6).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% emqx_time
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
time_now_to_(_) ->
|
||||
emqx_time:seed(),
|
||||
emqx_time:now_secs(),
|
||||
emqx_time:now_ms().
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% emqx_node
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
node_is_aliving(_) ->
|
||||
io:format("Node: ~p~n", [node()]),
|
||||
true = ekka_node:is_aliving(node()),
|
||||
false = ekka_node:is_aliving('x@127.0.0.1').
|
||||
|
||||
node_parse_name(_) ->
|
||||
'a@127.0.0.1' = ekka_node:parse_name("a@127.0.0.1"),
|
||||
'b@127.0.0.1' = ekka_node:parse_name("b").
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% base62 encode decode
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
base62_encode(_) ->
|
||||
<<"10">> = ?BASE62:decode(?BASE62:encode(<<"10">>)),
|
||||
<<"100">> = ?BASE62:decode(?BASE62:encode(<<"100">>)),
|
||||
<<"9999">> = ?BASE62:decode(?BASE62:encode(<<"9999">>)),
|
||||
<<"65535">> = ?BASE62:decode(?BASE62:encode(<<"65535">>)),
|
||||
<<X:128/unsigned-big-integer>> = emqx_guid:gen(),
|
||||
<<Y:128/unsigned-big-integer>> = emqx_guid:gen(),
|
||||
X = ?BASE62:decode(?BASE62:encode(X), integer),
|
||||
Y = ?BASE62:decode(?BASE62:encode(Y), integer),
|
||||
<<"helloworld">> = ?BASE62:decode(?BASE62:encode("helloworld")),
|
||||
"helloworld" = ?BASE62:decode(?BASE62:encode("helloworld", string), string).
|
|
@ -19,16 +19,10 @@
|
|||
-compile(export_all).
|
||||
-compile(nowarn_export_all).
|
||||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
-include_lib("common_test/include/ct.hrl").
|
||||
|
||||
-include("emqx.hrl").
|
||||
-include("emqx_mqtt.hrl").
|
||||
|
||||
all() ->
|
||||
[start_stop_listeners,
|
||||
restart_listeners].
|
||||
all() -> emqx_ct:all(?MODULE).
|
||||
|
||||
init_per_suite(Config) ->
|
||||
NewConfig = generate_config(),
|
||||
|
@ -41,11 +35,11 @@ end_per_suite(_Config) ->
|
|||
application:stop(esockd),
|
||||
application:stop(cowboy).
|
||||
|
||||
start_stop_listeners(_) ->
|
||||
t_start_stop_listeners(_) ->
|
||||
ok = emqx_listeners:start(),
|
||||
ok = emqx_listeners:stop().
|
||||
|
||||
restart_listeners(_) ->
|
||||
t_restart_listeners(_) ->
|
||||
ok = emqx_listeners:start(),
|
||||
ok = emqx_listeners:stop(),
|
||||
ok = emqx_listeners:restart(),
|
||||
|
@ -95,3 +89,4 @@ get_base_dir(Module) ->
|
|||
|
||||
get_base_dir() ->
|
||||
get_base_dir(?MODULE).
|
||||
|
||||
|
|
|
@ -16,7 +16,9 @@
|
|||
|
||||
-module(emqx_message_SUITE).
|
||||
|
||||
-include("emqx.hrl").
|
||||
-compile(export_all).
|
||||
-compile(nowarn_export_all).
|
||||
|
||||
-include("emqx_mqtt.hrl").
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
|
@ -32,17 +34,22 @@
|
|||
, suite/0
|
||||
]).
|
||||
|
||||
all() -> emqx_ct:all(?MODULE).
|
||||
|
||||
suite() ->
|
||||
[{ct_hooks, [cth_surefire]}, {timetrap, {seconds, 30}}].
|
||||
|
||||
t_make(_) ->
|
||||
Msg = emqx_message:make(<<"topic">>, <<"payload">>),
|
||||
?assertEqual(0, emqx_message:qos(Msg)),
|
||||
?assertEqual(?QOS_0, emqx_message:qos(Msg)),
|
||||
?assertEqual(undefined, emqx_message:from(Msg)),
|
||||
?assertEqual(<<"payload">>, emqx_message:payload(Msg)),
|
||||
Msg1 = emqx_message:make(<<"clientid">>, <<"topic">>, <<"payload">>),
|
||||
?assertEqual(0, emqx_message:qos(Msg1)),
|
||||
?assertEqual(?QOS_0, emqx_message:qos(Msg1)),
|
||||
?assertEqual(<<"topic">>, emqx_message:topic(Msg1)),
|
||||
Msg2 = emqx_message:make(<<"clientid">>, ?QOS_2, <<"topic">>, <<"payload">>),
|
||||
?assert(is_binary(emqx_message:id(Msg2))),
|
||||
?assertEqual(2, emqx_message:qos(Msg2)),
|
||||
?assertEqual(?QOS_2, emqx_message:qos(Msg2)),
|
||||
?assertEqual(<<"clientid">>, emqx_message:from(Msg2)),
|
||||
?assertEqual(<<"topic">>, emqx_message:topic(Msg2)),
|
||||
?assertEqual(<<"payload">>, emqx_message:payload(Msg2)).
|
||||
|
@ -86,21 +93,14 @@ t_expired(_) ->
|
|||
|
||||
t_to_map(_) ->
|
||||
Msg = emqx_message:make(<<"clientid">>, ?QOS_1, <<"topic">>, <<"payload">>),
|
||||
List = [{id, Msg#message.id},
|
||||
List = [{id, emqx_message:id(Msg)},
|
||||
{qos, ?QOS_1},
|
||||
{from, <<"clientid">>},
|
||||
{flags, #{dup => false}},
|
||||
{headers, #{}},
|
||||
{topic, <<"topic">>},
|
||||
{payload, <<"payload">>},
|
||||
{timestamp, Msg#message.timestamp}],
|
||||
{timestamp, emqx_message:timestamp(Msg)}],
|
||||
?assertEqual(List, emqx_message:to_list(Msg)),
|
||||
?assertEqual(maps:from_list(List), emqx_message:to_map(Msg)).
|
||||
|
||||
all() ->
|
||||
IsTestCase = fun("t_" ++ _) -> true; (_) -> false end,
|
||||
[F || {F, _A} <- module_info(exports), IsTestCase(atom_to_list(F))].
|
||||
|
||||
suite() ->
|
||||
[{ct_hooks, [cth_surefire]}, {timetrap, {seconds, 30}}].
|
||||
|
||||
|
|
|
@ -22,55 +22,64 @@
|
|||
-include("emqx_mqtt.hrl").
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
all() -> [t_inc_dec, t_inc_recv, t_inc_sent, t_trans].
|
||||
all() -> emqx_ct:all(?MODULE).
|
||||
|
||||
t_inc_dec(_) ->
|
||||
{ok, _} = emqx_metrics:start_link(),
|
||||
?assertEqual(0, emqx_metrics:val('bytes.received')),
|
||||
?assertEqual(0, emqx_metrics:val('messages.retained')),
|
||||
ok = emqx_metrics:inc('bytes.received'),
|
||||
ok = emqx_metrics:inc('bytes.received', 2),
|
||||
ok = emqx_metrics:inc('bytes.received', 2),
|
||||
?assertEqual(5, emqx_metrics:val('bytes.received')),
|
||||
ok = emqx_metrics:inc('messages.retained', 2),
|
||||
ok = emqx_metrics:inc('messages.retained', 2),
|
||||
?assertEqual(4, emqx_metrics:val('messages.retained')),
|
||||
ok = emqx_metrics:dec('messages.retained'),
|
||||
ok = emqx_metrics:dec('messages.retained', 1),
|
||||
?assertEqual(2, emqx_metrics:val('messages.retained')),
|
||||
ok = emqx_metrics:set('messages.retained', 3),
|
||||
?assertEqual(3, emqx_metrics:val('messages.retained')),
|
||||
ok = emqx_metrics:stop().
|
||||
with_metrics_server(
|
||||
fun() ->
|
||||
?assertEqual(0, emqx_metrics:val('bytes.received')),
|
||||
?assertEqual(0, emqx_metrics:val('messages.retained')),
|
||||
ok = emqx_metrics:inc('bytes.received'),
|
||||
ok = emqx_metrics:inc('bytes.received', 2),
|
||||
ok = emqx_metrics:inc('bytes.received', 2),
|
||||
?assertEqual(5, emqx_metrics:val('bytes.received')),
|
||||
ok = emqx_metrics:inc('messages.retained', 2),
|
||||
ok = emqx_metrics:inc('messages.retained', 2),
|
||||
?assertEqual(4, emqx_metrics:val('messages.retained')),
|
||||
ok = emqx_metrics:dec('messages.retained'),
|
||||
ok = emqx_metrics:dec('messages.retained', 1),
|
||||
?assertEqual(2, emqx_metrics:val('messages.retained')),
|
||||
ok = emqx_metrics:set('messages.retained', 3),
|
||||
?assertEqual(3, emqx_metrics:val('messages.retained'))
|
||||
end).
|
||||
|
||||
t_inc_recv(_) ->
|
||||
{ok, _} = emqx_metrics:start_link(),
|
||||
ok = emqx_metrics:inc_recv(?PACKET(?CONNECT)),
|
||||
?assertEqual(1, emqx_metrics:val('packets.received')),
|
||||
?assertEqual(1, emqx_metrics:val('packets.connect.received')),
|
||||
ok = emqx_metrics:stop().
|
||||
with_metrics_server(
|
||||
fun() ->
|
||||
ok = emqx_metrics:inc_recv(?PACKET(?CONNECT)),
|
||||
?assertEqual(1, emqx_metrics:val('packets.received')),
|
||||
?assertEqual(1, emqx_metrics:val('packets.connect.received'))
|
||||
end).
|
||||
|
||||
t_inc_sent(_) ->
|
||||
{ok, _} = emqx_metrics:start_link(),
|
||||
ok = emqx_metrics:inc_sent(?CONNACK_PACKET(0)),
|
||||
?assertEqual(1, emqx_metrics:val('packets.sent')),
|
||||
?assertEqual(1, emqx_metrics:val('packets.connack.sent')),
|
||||
ok = emqx_metrics:stop().
|
||||
with_metrics_server(
|
||||
fun() ->
|
||||
ok = emqx_metrics:inc_sent(?CONNACK_PACKET(0)),
|
||||
?assertEqual(1, emqx_metrics:val('packets.sent')),
|
||||
?assertEqual(1, emqx_metrics:val('packets.connack.sent'))
|
||||
end).
|
||||
|
||||
t_trans(_) ->
|
||||
with_metrics_server(
|
||||
fun() ->
|
||||
ok = emqx_metrics:trans(inc, 'bytes.received'),
|
||||
ok = emqx_metrics:trans(inc, 'bytes.received', 2),
|
||||
?assertEqual(0, emqx_metrics:val('bytes.received')),
|
||||
ok = emqx_metrics:trans(inc, 'messages.retained', 2),
|
||||
ok = emqx_metrics:trans(inc, 'messages.retained', 2),
|
||||
?assertEqual(0, emqx_metrics:val('messages.retained')),
|
||||
ok = emqx_metrics:commit(),
|
||||
?assertEqual(3, emqx_metrics:val('bytes.received')),
|
||||
?assertEqual(4, emqx_metrics:val('messages.retained')),
|
||||
ok = emqx_metrics:trans(dec, 'messages.retained'),
|
||||
ok = emqx_metrics:trans(dec, 'messages.retained', 1),
|
||||
?assertEqual(4, emqx_metrics:val('messages.retained')),
|
||||
ok = emqx_metrics:commit(),
|
||||
?assertEqual(2, emqx_metrics:val('messages.retained'))
|
||||
end).
|
||||
|
||||
with_metrics_server(Fun) ->
|
||||
{ok, _} = emqx_metrics:start_link(),
|
||||
ok = emqx_metrics:trans(inc, 'bytes.received'),
|
||||
ok = emqx_metrics:trans(inc, 'bytes.received', 2),
|
||||
?assertEqual(0, emqx_metrics:val('bytes.received')),
|
||||
ok = emqx_metrics:trans(inc, 'messages.retained', 2),
|
||||
ok = emqx_metrics:trans(inc, 'messages.retained', 2),
|
||||
?assertEqual(0, emqx_metrics:val('messages.retained')),
|
||||
ok = emqx_metrics:commit(),
|
||||
?assertEqual(3, emqx_metrics:val('bytes.received')),
|
||||
?assertEqual(4, emqx_metrics:val('messages.retained')),
|
||||
ok = emqx_metrics:trans(dec, 'messages.retained'),
|
||||
ok = emqx_metrics:trans(dec, 'messages.retained', 1),
|
||||
?assertEqual(4, emqx_metrics:val('messages.retained')),
|
||||
ok = emqx_metrics:commit(),
|
||||
?assertEqual(2, emqx_metrics:val('messages.retained')),
|
||||
_ = Fun(),
|
||||
ok = emqx_metrics:stop().
|
||||
|
||||
|
|
|
@ -14,7 +14,11 @@
|
|||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
-module(emqx_misc_tests).
|
||||
-module(emqx_misc_SUITE).
|
||||
|
||||
-compile(export_all).
|
||||
-compile(nowarn_export_all).
|
||||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
-define(SOCKOPTS, [binary,
|
||||
|
@ -23,8 +27,9 @@
|
|||
{backlog, 512},
|
||||
{nodelay, true}]).
|
||||
|
||||
all() -> emqx_ct:all(?MODULE).
|
||||
|
||||
t_merge_opts_test() ->
|
||||
t_merge_opts() ->
|
||||
Opts = emqx_misc:merge_opts(?SOCKOPTS, [raw,
|
||||
binary,
|
||||
{backlog, 1024},
|
||||
|
@ -41,33 +46,47 @@ t_merge_opts_test() ->
|
|||
{packet, raw},
|
||||
{reuseaddr, true}] = lists:sort(Opts).
|
||||
|
||||
timer_cancel_flush_test() ->
|
||||
t_timer_cancel_flush() ->
|
||||
Timer = emqx_misc:start_timer(0, foo),
|
||||
ok = emqx_misc:cancel_timer(Timer),
|
||||
receive {timeout, Timer, foo} -> error(unexpected)
|
||||
receive
|
||||
{timeout, Timer, foo} ->
|
||||
error(unexpected)
|
||||
after 0 -> ok
|
||||
end.
|
||||
|
||||
shutdown_disabled_test() ->
|
||||
t_shutdown_disabled() ->
|
||||
ok = drain(),
|
||||
self() ! foo,
|
||||
?assertEqual(continue, conn_proc_mng_policy(0)),
|
||||
?assertEqual(continue, emqx_misc:conn_proc_mng_policy(0)),
|
||||
receive foo -> ok end,
|
||||
?assertEqual(hibernate, conn_proc_mng_policy(0)).
|
||||
?assertEqual(hibernate, emqx_misc:conn_proc_mng_policy(0)).
|
||||
|
||||
message_queue_too_long_test() ->
|
||||
t_message_queue_too_long() ->
|
||||
ok = drain(),
|
||||
self() ! foo,
|
||||
self() ! bar,
|
||||
?assertEqual({shutdown, message_queue_too_long},
|
||||
conn_proc_mng_policy(1)),
|
||||
emqx_misc:conn_proc_mng_policy(1)),
|
||||
receive foo -> ok end,
|
||||
?assertEqual(continue, conn_proc_mng_policy(1)),
|
||||
?assertEqual(continue, emqx_misc:conn_proc_mng_policy(1)),
|
||||
receive bar -> ok end.
|
||||
|
||||
conn_proc_mng_policy(L) ->
|
||||
t_conn_proc_mng_policy(L) ->
|
||||
emqx_misc:conn_proc_mng_policy(#{message_queue_len => L}).
|
||||
|
||||
t_proc_name(_) ->
|
||||
'TODO'.
|
||||
|
||||
t_proc_stats(_) ->
|
||||
'TODO'.
|
||||
|
||||
t_drain_deliver(_) ->
|
||||
'TODO'.
|
||||
|
||||
t_drain_down(_) ->
|
||||
'TODO'.
|
||||
|
||||
%% drain self() msg queue for deterministic test behavior
|
||||
drain() ->
|
||||
_ = drain([]), % maybe log
|
|
@ -1,101 +0,0 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2019 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_mock_client).
|
||||
|
||||
-behaviour(gen_server).
|
||||
|
||||
-export([start_link/1, open_session/3, open_session/4,
|
||||
close_session/1, stop/1, get_last_message/1]).
|
||||
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
terminate/2, code_change/3]).
|
||||
|
||||
-record(state, {clean_start, client_id, client_pid, last_msg, session_pid}).
|
||||
|
||||
start_link(ClientId) ->
|
||||
gen_server:start_link(?MODULE, [ClientId], []).
|
||||
|
||||
open_session(ClientPid, ClientId, Zone) ->
|
||||
open_session(ClientPid, ClientId, Zone, _Attrs = #{}).
|
||||
|
||||
open_session(ClientPid, ClientId, Zone, Attrs0) ->
|
||||
Attrs1 = default_session_attributes(Zone, ClientId, ClientPid),
|
||||
Attrs = maps:merge(Attrs1, Attrs0),
|
||||
gen_server:call(ClientPid, {start_session, ClientPid, ClientId, Attrs}).
|
||||
|
||||
%% close session and terminate the client itself
|
||||
close_session(ClientPid) ->
|
||||
gen_server:call(ClientPid, stop_session, infinity).
|
||||
|
||||
stop(CPid) ->
|
||||
gen_server:call(CPid, stop, infinity).
|
||||
|
||||
get_last_message(Pid) ->
|
||||
gen_server:call(Pid, get_last_message, infinity).
|
||||
|
||||
init([ClientId]) ->
|
||||
erlang:process_flag(trap_exit, true),
|
||||
{ok, #state{clean_start = true,
|
||||
client_id = ClientId,
|
||||
last_msg = undefined
|
||||
}
|
||||
}.
|
||||
|
||||
handle_call({start_session, ClientPid, ClientId, Attrs}, _From, State) ->
|
||||
{ok, SessPid} = emqx_sm:open_session(Attrs),
|
||||
{reply, {ok, SessPid},
|
||||
State#state{clean_start = true,
|
||||
client_id = ClientId,
|
||||
client_pid = ClientPid,
|
||||
session_pid = SessPid
|
||||
}};
|
||||
handle_call(stop_session, _From, #state{session_pid = Pid} = State) ->
|
||||
is_pid(Pid) andalso is_process_alive(Pid) andalso emqx_sm:close_session(Pid),
|
||||
{stop, normal, ok, State#state{session_pid = undefined}};
|
||||
handle_call(get_last_message, _From, #state{last_msg = Msg} = State) ->
|
||||
{reply, Msg, State};
|
||||
handle_call(stop, _From, State) ->
|
||||
{stop, normal, ok, State};
|
||||
handle_call(_Request, _From, State) ->
|
||||
{reply, ok, State}.
|
||||
|
||||
handle_cast(_Msg, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
handle_info({deliver, Msg}, State) ->
|
||||
{noreply, State#state{last_msg = Msg}};
|
||||
handle_info(_Info, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
terminate(_Reason, _State) ->
|
||||
ok.
|
||||
|
||||
code_change(_OldVsn, State, _Extra) ->
|
||||
{ok, State}.
|
||||
|
||||
default_session_attributes(Zone, ClientId, ClientPid) ->
|
||||
#{zone => Zone,
|
||||
client_id => ClientId,
|
||||
conn_pid => ClientPid,
|
||||
clean_start => true,
|
||||
username => undefined,
|
||||
expiry_interval => 0,
|
||||
max_inflight => 0,
|
||||
topic_alias_maximum => 0,
|
||||
will_msg => undefined
|
||||
}.
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2019 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_mod_SUITE).
|
||||
|
||||
-compile(export_all).
|
||||
-compile(nowarn_export_all).
|
||||
|
||||
-include("emqx.hrl").
|
||||
|
||||
all() -> [mod_subscription_rep].
|
||||
|
||||
mod_subscription_rep(_) -> ok.
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2019 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_mod_rewrite_tests).
|
||||
|
||||
-include_lib("emqx.hrl").
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
|
||||
rules() ->
|
||||
Rawrules1 = "x/# ^x/y/(.+)$ z/y/$1",
|
||||
Rawrules2 = "y/+/z/# ^y/(.+)/z/(.+)$ y/z/$2",
|
||||
Rawrules = [Rawrules1, Rawrules2],
|
||||
Rules = lists:map(fun(Rule) ->
|
||||
[Topic, Re, Dest] = string:tokens(Rule, " "),
|
||||
{rewrite,
|
||||
list_to_binary(Topic),
|
||||
list_to_binary(Re),
|
||||
list_to_binary(Dest)}
|
||||
end, Rawrules),
|
||||
lists:map(fun({rewrite, Topic, Re, Dest}) ->
|
||||
{ok, MP} = re:compile(Re),
|
||||
{rewrite, Topic, MP, Dest}
|
||||
end, Rules).
|
||||
|
||||
rewrite_subscribe_test() ->
|
||||
Rules = rules(),
|
||||
io:format("Rules: ~p",[Rules]),
|
||||
?assertEqual({ok, [{<<"test">>, opts}]},
|
||||
emqx_mod_rewrite:rewrite_subscribe(credentials, [{<<"test">>, opts}], Rules)),
|
||||
?assertEqual({ok, [{<<"z/y/test">>, opts}]},
|
||||
emqx_mod_rewrite:rewrite_subscribe(credentials, [{<<"x/y/test">>, opts}], Rules)),
|
||||
?assertEqual({ok, [{<<"y/z/test_topic">>, opts}]},
|
||||
emqx_mod_rewrite:rewrite_subscribe(credentials, [{<<"y/test/z/test_topic">>, opts}], Rules)).
|
||||
|
||||
rewrite_unsubscribe_test() ->
|
||||
Rules = rules(),
|
||||
?assertEqual({ok, [{<<"test">>, opts}]},
|
||||
emqx_mod_rewrite:rewrite_subscribe(credentials, [{<<"test">>, opts}], Rules)),
|
||||
?assertEqual({ok, [{<<"z/y/test">>, opts}]},
|
||||
emqx_mod_rewrite:rewrite_subscribe(credentials, [{<<"x/y/test">>, opts}], Rules)),
|
||||
?assertEqual({ok, [{<<"y/z/test_topic">>, opts}]},
|
||||
emqx_mod_rewrite:rewrite_subscribe(credentials, [{<<"y/test/z/test_topic">>, opts}], Rules)).
|
||||
|
||||
rewrite_publish_test() ->
|
||||
Rules = rules(),
|
||||
?assertMatch({ok, #message{topic = <<"test">>}},
|
||||
emqx_mod_rewrite:rewrite_publish(#message{topic = <<"test">>}, Rules)),
|
||||
?assertMatch({ok, #message{topic = <<"z/y/test">>}},
|
||||
emqx_mod_rewrite:rewrite_publish(#message{topic = <<"x/y/test">>}, Rules)),
|
||||
?assertMatch({ok, #message{topic = <<"y/z/test_topic">>}},
|
||||
emqx_mod_rewrite:rewrite_publish(#message{topic = <<"y/test/z/test_topic">>}, Rules)).
|
|
@ -1,45 +0,0 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2019 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_mod_sup_SUITE).
|
||||
|
||||
-compile(export_all).
|
||||
-compile(nowarn_export_all).
|
||||
|
||||
-include("emqx.hrl").
|
||||
|
||||
all() -> [t_child_all].
|
||||
|
||||
start_link() ->
|
||||
Pid = spawn_link(?MODULE, echo, [0]),
|
||||
{ok, Pid}.
|
||||
|
||||
echo(State) ->
|
||||
receive
|
||||
{From, Req} ->
|
||||
ct:pal("======from:~p, req:~p", [From, Req]),
|
||||
From ! Req,
|
||||
echo(State)
|
||||
end.
|
||||
|
||||
t_child_all(_) ->
|
||||
{ok, Pid} = emqx_mod_sup:start_link(),
|
||||
{ok, Child} = emqx_mod_sup:start_child(?MODULE, worker),
|
||||
timer:sleep(10),
|
||||
Child ! {self(), hi},
|
||||
receive hi -> ok after 100 -> ct:fail({timeout, wait_echo}) end,
|
||||
ok = emqx_mod_sup:stop_child(?MODULE),
|
||||
exit(Pid, normal).
|
|
@ -19,20 +19,49 @@
|
|||
-compile(export_all).
|
||||
-compile(nowarn_export_all).
|
||||
|
||||
-include("emqx.hrl").
|
||||
-include("emqx_mqtt.hrl").
|
||||
-import(emqx_mountpoint,
|
||||
[ mount/2
|
||||
, unmount/2
|
||||
, replvar/2
|
||||
]).
|
||||
|
||||
-include("emqx.hrl").
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
all() -> [t_mount_unmount, t_replvar].
|
||||
all() -> emqx_ct:all(?MODULE).
|
||||
|
||||
t_mount_unmount(_) ->
|
||||
t_mount(_) ->
|
||||
Msg = emqx_message:make(<<"clientid">>, <<"topic">>, <<"payload">>),
|
||||
Msg2 = emqx_mountpoint:mount(<<"mount">>, Msg),
|
||||
?assertEqual(<<"mounttopic">>, Msg2#message.topic),
|
||||
TopicFilter = [{<<"mounttopic">>, #{qos => ?QOS_2}}],
|
||||
TopicFilter = emqx_mountpoint:mount(<<"mount">>, [{<<"topic">>, #{qos => ?QOS_2}}]),
|
||||
Msg = emqx_mountpoint:unmount(<<"mount">>, Msg2).
|
||||
TopicFilters = [{<<"topic">>, #{qos => 2}}],
|
||||
?assertEqual(<<"topic">>, mount(undefined, <<"topic">>)),
|
||||
?assertEqual(Msg, mount(undefined, Msg)),
|
||||
?assertEqual(TopicFilters, mount(undefined, TopicFilters)),
|
||||
?assertEqual(<<"device/1/topic">>,
|
||||
mount(<<"device/1/">>, <<"topic">>)),
|
||||
?assertEqual(Msg#message{topic = <<"device/1/topic">>},
|
||||
mount(<<"device/1/">>, Msg)),
|
||||
?assertEqual([{<<"device/1/topic">>, #{qos => 2}}],
|
||||
mount(<<"device/1/">>, TopicFilters)).
|
||||
|
||||
t_unmount(_) ->
|
||||
Msg = emqx_message:make(<<"clientid">>, <<"device/1/topic">>, <<"payload">>),
|
||||
?assertEqual(<<"topic">>, unmount(undefined, <<"topic">>)),
|
||||
?assertEqual(Msg, unmount(undefined, Msg)),
|
||||
?assertEqual(<<"topic">>, unmount(<<"device/1/">>, <<"device/1/topic">>)),
|
||||
?assertEqual(Msg#message{topic = <<"topic">>}, unmount(<<"device/1/">>, Msg)),
|
||||
?assertEqual(<<"device/1/topic">>, unmount(<<"device/2/">>, <<"device/1/topic">>)),
|
||||
?assertEqual(Msg#message{topic = <<"device/1/topic">>}, unmount(<<"device/2/">>, Msg)).
|
||||
|
||||
t_replvar(_) ->
|
||||
<<"mount/test/clientid">> = emqx_mountpoint:replvar(<<"mount/%u/%c">>, #{client_id => <<"clientid">>, username => <<"test">>}).
|
||||
?assertEqual(undefined, replvar(undefined, #{})),
|
||||
?assertEqual(<<"mount/user/clientid/">>,
|
||||
replvar(<<"mount/%u/%c/">>,
|
||||
#{client_id => <<"clientid">>,
|
||||
username => <<"user">>
|
||||
})),
|
||||
?assertEqual(<<"mount/%u/clientid/">>,
|
||||
replvar(<<"mount/%u/%c/">>,
|
||||
#{client_id => <<"clientid">>,
|
||||
username => undefined
|
||||
})).
|
||||
|
||||
|
|
|
@ -1,133 +0,0 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2019 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_mqtt_caps_SUITE).
|
||||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
-include("emqx.hrl").
|
||||
-include("emqx_mqtt.hrl").
|
||||
|
||||
%% CT
|
||||
-compile(export_all).
|
||||
-compile(nowarn_export_all).
|
||||
|
||||
all() -> [t_get_set_caps, t_check_pub, t_check_sub].
|
||||
|
||||
t_get_set_caps(_) ->
|
||||
{ok, _} = emqx_zone:start_link(),
|
||||
Caps = #{
|
||||
max_packet_size => ?MAX_PACKET_SIZE,
|
||||
max_clientid_len => ?MAX_CLIENTID_LEN,
|
||||
max_topic_alias => 0,
|
||||
max_topic_levels => 0,
|
||||
max_qos_allowed => ?QOS_2,
|
||||
mqtt_retain_available => true,
|
||||
mqtt_shared_subscription => true,
|
||||
mqtt_wildcard_subscription => true
|
||||
},
|
||||
Caps2 = Caps#{max_packet_size => 1048576},
|
||||
case emqx_mqtt_caps:get_caps(zone) of
|
||||
Caps -> ok;
|
||||
Caps2 -> ok
|
||||
end,
|
||||
PubCaps = #{
|
||||
max_qos_allowed => ?QOS_2,
|
||||
mqtt_retain_available => true,
|
||||
max_topic_alias => 0
|
||||
},
|
||||
PubCaps = emqx_mqtt_caps:get_caps(zone, publish),
|
||||
NewPubCaps = PubCaps#{max_qos_allowed => ?QOS_1},
|
||||
emqx_zone:set_env(zone, '$mqtt_pub_caps', NewPubCaps),
|
||||
timer:sleep(100),
|
||||
NewPubCaps = emqx_mqtt_caps:get_caps(zone, publish),
|
||||
SubCaps = #{
|
||||
max_topic_levels => 0,
|
||||
max_qos_allowed => ?QOS_2,
|
||||
mqtt_shared_subscription => true,
|
||||
mqtt_wildcard_subscription => true
|
||||
},
|
||||
SubCaps = emqx_mqtt_caps:get_caps(zone, subscribe),
|
||||
emqx_zone:stop().
|
||||
|
||||
t_check_pub(_) ->
|
||||
{ok, _} = emqx_zone:start_link(),
|
||||
PubCaps = #{
|
||||
max_qos_allowed => ?QOS_1,
|
||||
mqtt_retain_available => false,
|
||||
max_topic_alias => 4
|
||||
},
|
||||
emqx_zone:set_env(zone, '$mqtt_pub_caps', PubCaps),
|
||||
timer:sleep(100),
|
||||
ct:log("~p", [emqx_mqtt_caps:get_caps(zone, publish)]),
|
||||
BadPubProps1 = #{
|
||||
qos => ?QOS_2,
|
||||
retain => false
|
||||
},
|
||||
{error, ?RC_QOS_NOT_SUPPORTED} = emqx_mqtt_caps:check_pub(zone, BadPubProps1),
|
||||
BadPubProps2 = #{
|
||||
qos => ?QOS_1,
|
||||
retain => true
|
||||
},
|
||||
{error, ?RC_RETAIN_NOT_SUPPORTED} = emqx_mqtt_caps:check_pub(zone, BadPubProps2),
|
||||
BadPubProps3 = #{
|
||||
qos => ?QOS_1,
|
||||
retain => false,
|
||||
topic_alias => 5
|
||||
},
|
||||
{error, ?RC_TOPIC_ALIAS_INVALID} = emqx_mqtt_caps:check_pub(zone, BadPubProps3),
|
||||
PubProps = #{
|
||||
qos => ?QOS_1,
|
||||
retain => false
|
||||
},
|
||||
ok = emqx_mqtt_caps:check_pub(zone, PubProps),
|
||||
emqx_zone:stop().
|
||||
|
||||
t_check_sub(_) ->
|
||||
{ok, _} = emqx_zone:start_link(),
|
||||
|
||||
Opts = #{qos => ?QOS_2, share => true, rc => 0},
|
||||
Caps = #{
|
||||
max_topic_levels => 0,
|
||||
max_qos_allowed => ?QOS_2,
|
||||
mqtt_shared_subscription => true,
|
||||
mqtt_wildcard_subscription => true
|
||||
},
|
||||
|
||||
ok = do_check_sub([{<<"client/stat">>, Opts}], [{<<"client/stat">>, Opts}]),
|
||||
ok = do_check_sub(Caps#{max_qos_allowed => ?QOS_1}, [{<<"client/stat">>, Opts}], [{<<"client/stat">>, Opts#{qos => ?QOS_1}}]),
|
||||
ok = do_check_sub(Caps#{max_topic_levels => 1},
|
||||
[{<<"client/stat">>, Opts}],
|
||||
[{<<"client/stat">>, Opts#{rc => ?RC_TOPIC_FILTER_INVALID}}]),
|
||||
ok = do_check_sub(Caps#{mqtt_shared_subscription => false},
|
||||
[{<<"client/stat">>, Opts}],
|
||||
[{<<"client/stat">>, Opts#{rc => ?RC_SHARED_SUBSCRIPTIONS_NOT_SUPPORTED}}]),
|
||||
|
||||
ok = do_check_sub(Caps#{mqtt_wildcard_subscription => false},
|
||||
[{<<"vlient/+/dsofi">>, Opts}],
|
||||
[{<<"vlient/+/dsofi">>, Opts#{rc => ?RC_WILDCARD_SUBSCRIPTIONS_NOT_SUPPORTED}}]),
|
||||
emqx_zone:stop().
|
||||
|
||||
|
||||
|
||||
do_check_sub(TopicFilters, Topics) ->
|
||||
{ok, Topics} = emqx_mqtt_caps:check_sub(zone, TopicFilters),
|
||||
ok.
|
||||
do_check_sub(Caps, TopicFilters, Topics) ->
|
||||
emqx_zone:set_env(zone, '$mqtt_sub_caps', Caps),
|
||||
timer:sleep(100),
|
||||
{_, Topics} = emqx_mqtt_caps:check_sub(zone, TopicFilters),
|
||||
ok.
|
|
@ -1,117 +0,0 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2019 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_mqtt_packet_SUITE).
|
||||
|
||||
-compile(export_all).
|
||||
-compile(nowarn_export_all).
|
||||
|
||||
-import(emqx_frame, [serialize/1]).
|
||||
|
||||
-include("emqx_mqtt.hrl").
|
||||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
-define(INVALID_RESERVED, 1).
|
||||
|
||||
-define(CONNECT_INVALID_PACKET(Var),
|
||||
#mqtt_packet{header = #mqtt_packet_header{type = ?INVALID_RESERVED},
|
||||
variable = Var}).
|
||||
|
||||
-define(CASE1_PROTOCOL_NAME, ?CONNECT_PACKET(#mqtt_packet_connect{
|
||||
proto_name = <<"MQTC">>,
|
||||
client_id = <<"mqtt_protocol_name">>,
|
||||
username = <<"admin">>,
|
||||
password = <<"public">>})).
|
||||
|
||||
-define(CASE2_PROTOCAL_VER, ?CONNECT_PACKET(#mqtt_packet_connect{
|
||||
client_id = <<"mqtt_client">>,
|
||||
proto_ver = 6,
|
||||
username = <<"admin">>,
|
||||
password = <<"public">>})).
|
||||
|
||||
-define(CASE3_PROTOCAL_INVALID_RESERVED, ?CONNECT_INVALID_PACKET(#mqtt_packet_connect{
|
||||
client_id = <<"mqtt_client">>,
|
||||
proto_ver = 5,
|
||||
username = <<"admin">>,
|
||||
password = <<"public">>})).
|
||||
|
||||
-define(PROTOCOL5, ?CONNECT_PACKET(#mqtt_packet_connect{
|
||||
proto_ver = 5,
|
||||
keepalive = 60,
|
||||
properties = #{'Message-Expiry-Interval' => 3600},
|
||||
client_id = <<"mqtt_client">>,
|
||||
will_topic = <<"will_tipic">>,
|
||||
will_payload = <<"will message">>,
|
||||
username = <<"admin">>,
|
||||
password = <<"public">>})).
|
||||
|
||||
|
||||
|
||||
all() -> [{group, connect}].
|
||||
|
||||
groups() -> [{connect, [sequence],
|
||||
[case1_protocol_name,
|
||||
case2_protocol_ver%,
|
||||
%TOTO case3_invalid_reserved
|
||||
]}].
|
||||
|
||||
init_per_suite(Config) ->
|
||||
emqx_ct_helpers:start_apps([]),
|
||||
Config.
|
||||
|
||||
end_per_suite(_Config) ->
|
||||
emqx_ct_helpers:stop_apps([]).
|
||||
|
||||
init_per_group(_Group, Config) ->
|
||||
Config.
|
||||
|
||||
end_per_group(_Group, _Config) ->
|
||||
ok.
|
||||
|
||||
case1_protocol_name(_) ->
|
||||
{ok, Sock} = emqx_client_sock:connect({127,0,0,1}, 1883, [binary, {packet, raw}, {active, false}], 3000),
|
||||
MqttPacket = serialize(?CASE1_PROTOCOL_NAME),
|
||||
emqx_client_sock:send(Sock, MqttPacket),
|
||||
{ok, Data} = gen_tcp:recv(Sock, 0),
|
||||
{ok, ?CONNACK_PACKET(?CONNACK_PROTO_VER), <<>>, _} = raw_recv_pase(Data),
|
||||
Disconnect = gen_tcp:recv(Sock, 0),
|
||||
?assertEqual({error, closed}, Disconnect).
|
||||
|
||||
case2_protocol_ver(_) ->
|
||||
{ok, Sock} = emqx_client_sock:connect({127,0,0,1}, 1883, [binary, {packet, raw}, {active, false}], 3000),
|
||||
Packet = serialize(?CASE2_PROTOCAL_VER),
|
||||
emqx_client_sock:send(Sock, Packet),
|
||||
{ok, Data} = gen_tcp:recv(Sock, 0),
|
||||
%% case1 Unacceptable protocol version
|
||||
{ok, ?CONNACK_PACKET(?CONNACK_PROTO_VER), <<>>, _} = raw_recv_pase(Data),
|
||||
Disconnect = gen_tcp:recv(Sock, 0),
|
||||
?assertEqual({error, closed}, Disconnect).
|
||||
|
||||
case3_invalid_reserved(_) ->
|
||||
{ok, Sock} = emqx_client_sock:connect({127,0,0,1}, 1883, [binary, {packet, raw}, {active, false}], 3000),
|
||||
Packet = serialize(?CASE3_PROTOCAL_INVALID_RESERVED),
|
||||
emqx_client_sock:send(Sock, Packet),
|
||||
{ok, Data} = gen_tcp:recv(Sock, 0),
|
||||
%% case1 Unacceptable protocol version
|
||||
ct:log("Data:~p~n", [raw_recv_pase(Data)]),
|
||||
{ok, ?CONNACK_PACKET(?CONNACK_PROTO_VER), _} = raw_recv_pase(Data),
|
||||
Disconnect = gen_tcp:recv(Sock, 0),
|
||||
?assertEqual({error, closed}, Disconnect).
|
||||
|
||||
raw_recv_pase(P) ->
|
||||
emqx_frame:parse(P, {none, #{max_packet_size => ?MAX_PACKET_SIZE,
|
||||
version => ?MQTT_PROTO_V4} }).
|
|
@ -21,9 +21,21 @@
|
|||
|
||||
-include("emqx_mqtt.hrl").
|
||||
|
||||
all() -> [t_mqtt_properties_all].
|
||||
all() -> emqx_ct:all(?MODULE).
|
||||
|
||||
t_mqtt_properties_all(_) ->
|
||||
t_id(_) ->
|
||||
'TODO'.
|
||||
|
||||
t_name(_) ->
|
||||
'TODO'.
|
||||
|
||||
t_filter(_) ->
|
||||
'TODO'.
|
||||
|
||||
t_validate(_) ->
|
||||
'TODO'.
|
||||
|
||||
deprecated_mqtt_properties_all(_) ->
|
||||
Props = emqx_mqtt_props:filter(?CONNECT, #{'Session-Expiry-Interval' => 1, 'Maximum-Packet-Size' => 255}),
|
||||
ok = emqx_mqtt_props:validate(Props),
|
||||
#{} = emqx_mqtt_props:filter(?CONNECT, #{'Maximum-QoS' => ?QOS_2}).
|
||||
|
|
|
@ -26,8 +26,7 @@
|
|||
|
||||
-define(Q, emqx_mqueue).
|
||||
|
||||
all() -> [t_in, t_in_qos0, t_out, t_simple_mqueue, t_infinity_simple_mqueue,
|
||||
t_priority_mqueue, t_infinity_priority_mqueue].
|
||||
all() -> emqx_ct:all(?MODULE).
|
||||
|
||||
t_in(_) ->
|
||||
Opts = #{max_len => 5, store_qos0 => true},
|
||||
|
@ -130,15 +129,18 @@ t_infinity_priority_mqueue(_) ->
|
|||
?assertEqual(510, ?Q:len(Qx)),
|
||||
?assertEqual([{len, 510}, {max_len, 0}, {dropped, 0}], ?Q:stats(Qx)).
|
||||
|
||||
t_priority_mqueue2(_) ->
|
||||
Opts = #{max_length => 2, store_qos0 => false},
|
||||
Q = ?Q:init("priority_queue2_test", Opts),
|
||||
%%TODO: fixme later
|
||||
t_length_priority_mqueue(_) ->
|
||||
Opts = #{max_len => 2,
|
||||
store_qos0 => false
|
||||
},
|
||||
Q = ?Q:init(Opts),
|
||||
2 = ?Q:max_len(Q),
|
||||
{_, Q1} = ?Q:in(#message{topic = <<"x">>, qos = 1, payload = <<1>>}, Q),
|
||||
{_, Q2} = ?Q:in(#message{topic = <<"x">>, qos = 1, payload = <<2>>}, Q1),
|
||||
{_, Q3} = ?Q:in(#message{topic = <<"y">>, qos = 1, payload = <<3>>}, Q2),
|
||||
{_, Q4} = ?Q:in(#message{topic = <<"y">>, qos = 1, payload = <<4>>}, Q3),
|
||||
4 = ?Q:len(Q4),
|
||||
?assertEqual(2, ?Q:len(Q4)),
|
||||
{{value, _Val}, Q5} = ?Q:out(Q4),
|
||||
3 = ?Q:len(Q5).
|
||||
?assertEqual(1, ?Q:len(Q5)).
|
||||
|
||||
|
|
|
@ -21,9 +21,7 @@
|
|||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
-include_lib("common_test/include/ct.hrl").
|
||||
|
||||
all() -> [t_api].
|
||||
all() -> emqx_ct:all(?MODULE).
|
||||
|
||||
init_per_suite(Config) ->
|
||||
application:ensure_all_started(os_mon),
|
||||
|
@ -56,3 +54,4 @@ t_api(_) ->
|
|||
% timer:sleep(3000),
|
||||
% ?assertEqual(false, lists:keymember(cpu_high_watermark, 1, alarm_handler:get_alarms())),
|
||||
ok.
|
||||
|
||||
|
|
|
@ -20,35 +20,28 @@
|
|||
-compile(nowarn_export_all).
|
||||
|
||||
-include("emqx.hrl").
|
||||
|
||||
-include("emqx_mqtt.hrl").
|
||||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
all() ->
|
||||
[
|
||||
packet_proto_name,
|
||||
packet_type_name,
|
||||
packet_validate,
|
||||
packet_message,
|
||||
packet_format,
|
||||
packet_will_msg
|
||||
].
|
||||
all() -> emqx_ct:all(?MODULE).
|
||||
|
||||
packet_proto_name(_) ->
|
||||
t_proto_name(_) ->
|
||||
?assertEqual(<<"MQIsdp">>, emqx_packet:protocol_name(3)),
|
||||
?assertEqual(<<"MQTT">>, emqx_packet:protocol_name(4)),
|
||||
?assertEqual(<<"MQTT">>, emqx_packet:protocol_name(5)).
|
||||
|
||||
packet_type_name(_) ->
|
||||
?assertEqual('CONNECT', emqx_packet:type_name(?CONNECT)),
|
||||
t_type_name(_) ->
|
||||
?assertEqual('CONNECT', emqx_packet:type_name(?CONNECT)),
|
||||
?assertEqual('UNSUBSCRIBE', emqx_packet:type_name(?UNSUBSCRIBE)).
|
||||
|
||||
packet_validate(_) ->
|
||||
?assert(emqx_packet:validate(?SUBSCRIBE_PACKET(15, #{'Subscription-Identifier' => 1}, [{<<"topic">>, #{qos => ?QOS_0}}]))),
|
||||
t_validate(_) ->
|
||||
?assert(emqx_packet:validate(?SUBSCRIBE_PACKET(15, #{'Subscription-Identifier' => 1},
|
||||
[{<<"topic">>, #{qos => ?QOS_0}}]))),
|
||||
?assert(emqx_packet:validate(?UNSUBSCRIBE_PACKET(89, [<<"topic">>]))),
|
||||
?assert(emqx_packet:validate(?CONNECT_PACKET(#mqtt_packet_connect{}))),
|
||||
?assert(emqx_packet:validate(?PUBLISH_PACKET(1, <<"topic">>, 1, #{'Response-Topic' => <<"responsetopic">>, 'Topic-Alias' => 1}, <<"payload">>))),
|
||||
Props = #{'Response-Topic' => <<"responsetopic">>, 'Topic-Alias' => 1},
|
||||
?assert(emqx_packet:validate(?PUBLISH_PACKET(?QOS_1, <<"topic">>, 1, Props, <<"payload">>))),
|
||||
?assert(emqx_packet:validate(?CONNECT_PACKET(#mqtt_packet_connect{properties = #{'Receive-Maximum' => 1}}))),
|
||||
?assertError(subscription_identifier_invalid,
|
||||
emqx_packet:validate(
|
||||
|
@ -89,7 +82,7 @@ packet_validate(_) ->
|
|||
properties =
|
||||
#{'Receive-Maximum' => 0}}))).
|
||||
|
||||
packet_message(_) ->
|
||||
t_from_to_message(_) ->
|
||||
Pkt = #mqtt_packet{header = #mqtt_packet_header{type = ?PUBLISH,
|
||||
qos = ?QOS_0,
|
||||
retain = false,
|
||||
|
@ -111,7 +104,7 @@ packet_message(_) ->
|
|||
Msg5 = Msg4#message{timestamp = Msg3#message.timestamp, id = Msg3#message.id},
|
||||
Msg5 = Msg3.
|
||||
|
||||
packet_format(_) ->
|
||||
t_packet_format(_) ->
|
||||
io:format("~s", [emqx_packet:format(?CONNECT_PACKET(#mqtt_packet_connect{}))]),
|
||||
io:format("~s", [emqx_packet:format(?CONNACK_PACKET(?CONNACK_SERVER))]),
|
||||
io:format("~s", [emqx_packet:format(?PUBLISH_PACKET(?QOS_1, 1))]),
|
||||
|
@ -123,15 +116,17 @@ packet_format(_) ->
|
|||
io:format("~s", [emqx_packet:format(?UNSUBSCRIBE_PACKET(89, [<<"t">>, <<"t2">>]))]),
|
||||
io:format("~s", [emqx_packet:format(?UNSUBACK_PACKET(90))]).
|
||||
|
||||
packet_will_msg(_) ->
|
||||
Pkt = #mqtt_packet_connect{ will_flag = true,
|
||||
client_id = <<"clientid">>,
|
||||
username = "test",
|
||||
will_retain = true,
|
||||
will_qos = ?QOS_2,
|
||||
will_topic = <<"topic">>,
|
||||
will_props = #{},
|
||||
will_payload = <<"payload">>},
|
||||
t_will_msg(_) ->
|
||||
Pkt = #mqtt_packet_connect{will_flag = true,
|
||||
client_id = <<"clientid">>,
|
||||
username = "test",
|
||||
will_retain = true,
|
||||
will_qos = ?QOS_2,
|
||||
will_topic = <<"topic">>,
|
||||
will_props = #{},
|
||||
will_payload = <<"payload">>
|
||||
},
|
||||
Msg = emqx_packet:will_msg(Pkt),
|
||||
?assertEqual(<<"clientid">>, Msg#message.from),
|
||||
?assertEqual(<<"topic">>, Msg#message.topic).
|
||||
|
||||
|
|
|
@ -21,9 +21,9 @@
|
|||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
all() -> [update_counter].
|
||||
all() -> emqx_ct:all(?MODULE).
|
||||
|
||||
update_counter(_) ->
|
||||
t_update_counter(_) ->
|
||||
?assertEqual(undefined, emqx_pd:update_counter(bytes, 1)),
|
||||
?assertEqual(1, emqx_pd:update_counter(bytes, 1)),
|
||||
?assertEqual(2, emqx_pd:update_counter(bytes, 1)),
|
||||
|
|
|
@ -21,8 +21,7 @@
|
|||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
all() ->
|
||||
[t_monitor, t_find, t_erase].
|
||||
all() -> emqx_ct:all(?MODULE).
|
||||
|
||||
t_monitor(_) ->
|
||||
PMon = emqx_pmon:new(),
|
||||
|
|
|
@ -19,20 +19,23 @@
|
|||
-compile(export_all).
|
||||
-compile(nowarn_export_all).
|
||||
|
||||
-include("emqx_mqtt.hrl").
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
all() ->
|
||||
[
|
||||
{group, submit_case},
|
||||
{group, async_submit_case},
|
||||
[{group, submit},
|
||||
{group, async_submit},
|
||||
t_unexpected
|
||||
].
|
||||
|
||||
groups() ->
|
||||
[
|
||||
{submit_case, [sequence], [submit_mfa, submit_fa]},
|
||||
{async_submit_case, [sequence], [async_submit_mfa, async_submit_crash]}
|
||||
[{submit, [sequence],
|
||||
[t_submit_mfa,
|
||||
t_submit_fa
|
||||
]},
|
||||
{async_submit, [sequence],
|
||||
[t_async_submit_mfa,
|
||||
t_async_submit_crash
|
||||
]}
|
||||
].
|
||||
|
||||
init_per_suite(Config) ->
|
||||
|
@ -48,22 +51,28 @@ init_per_testcase(_, Config) ->
|
|||
|
||||
end_per_testcase(_, Config) ->
|
||||
Sup = proplists:get_value(pool_sup, Config),
|
||||
%% ???
|
||||
exit(Sup, normal).
|
||||
|
||||
submit_mfa(_Config) ->
|
||||
t_submit_mfa(_Config) ->
|
||||
Result = emqx_pool:submit({?MODULE, test_mfa, []}),
|
||||
?assertEqual(15, Result).
|
||||
|
||||
submit_fa(_Config) ->
|
||||
Fun = fun(X) -> case X rem 2 of 0 -> {true, X div 2}; _ -> false end end,
|
||||
t_submit_fa(_Config) ->
|
||||
Fun = fun(X) ->
|
||||
case X rem 2 of
|
||||
0 -> {true, X div 2};
|
||||
_ -> false
|
||||
end
|
||||
end,
|
||||
Result = emqx_pool:submit(Fun, [2]),
|
||||
?assertEqual({true, 1}, Result).
|
||||
|
||||
async_submit_mfa(_Config) ->
|
||||
t_async_submit_mfa(_Config) ->
|
||||
emqx_pool:async_submit({?MODULE, test_mfa, []}),
|
||||
emqx_pool:async_submit(fun ?MODULE:test_mfa/0, []).
|
||||
|
||||
async_submit_crash(_) ->
|
||||
t_async_submit_crash(_) ->
|
||||
emqx_pool:async_submit(fun() -> error(unexpected_error) end).
|
||||
|
||||
t_unexpected(_) ->
|
||||
|
|
|
@ -16,15 +16,15 @@
|
|||
|
||||
-module(emqx_pqueue_SUITE).
|
||||
|
||||
-include("emqx_mqtt.hrl").
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
-compile(export_all).
|
||||
-compile(nowarn_export_all).
|
||||
|
||||
-define(PQ, emqx_pqueue).
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
all() -> [t_priority_queue_plen, t_priority_queue_out2, t_priority_queues].
|
||||
-define(PQ, emqx_pqueue).
|
||||
-define(SUITE, ?MODULE).
|
||||
|
||||
all() -> emqx_ct:all(?SUITE).
|
||||
|
||||
t_priority_queue_plen(_) ->
|
||||
Q = ?PQ:new(),
|
||||
|
@ -87,7 +87,7 @@ t_priority_queues(_) ->
|
|||
|
||||
[{1, c}, {1, d}, {0, a}, {0, b}] = ?PQ:to_list(PQueue4),
|
||||
PQueue4 = ?PQ:from_list([{1, c}, {1, d}, {0, a}, {0, b}]),
|
||||
|
||||
|
||||
empty = ?PQ:highest(?PQ:new()),
|
||||
0 = ?PQ:highest(PQueue1),
|
||||
1 = ?PQ:highest(PQueue4),
|
||||
|
@ -122,4 +122,3 @@ t_priority_queues(_) ->
|
|||
{pqueue,[{-1,{queue,[f],[d,f,d],4}},
|
||||
{0,{queue,[b],[a,b,a],4}}]} = ?PQ:join(PQueue8, PQueue8).
|
||||
|
||||
|
||||
|
|
|
@ -1,613 +0,0 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2019 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_protocol_SUITE).
|
||||
|
||||
-compile(export_all).
|
||||
-compile(nowarn_export_all).
|
||||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
-include_lib("common_test/include/ct.hrl").
|
||||
|
||||
-include("emqx_mqtt.hrl").
|
||||
|
||||
-define(TOPICS, [<<"TopicA">>, <<"TopicA/B">>, <<"Topic/C">>, <<"TopicA/C">>,
|
||||
<<"/TopicA">>]).
|
||||
|
||||
-define(CLIENT, ?CONNECT_PACKET(#mqtt_packet_connect{
|
||||
client_id = <<"mqtt_client">>,
|
||||
username = <<"emqx">>,
|
||||
password = <<"public">>})).
|
||||
|
||||
all() ->
|
||||
[{group, mqtt_common},
|
||||
{group, mqttv4},
|
||||
{group, mqttv5},
|
||||
{group, acl},
|
||||
{group, frame_partial}].
|
||||
|
||||
groups() ->
|
||||
[{mqtt_common, [sequence],
|
||||
[will_topic_check,
|
||||
will_acl_check]},
|
||||
{mqttv4, [sequence],
|
||||
[connect_v4,
|
||||
subscribe_v4]},
|
||||
{mqttv5, [sequence],
|
||||
[connect_v5,
|
||||
subscribe_v5]},
|
||||
{acl, [sequence],
|
||||
[acl_deny_action_ct]},
|
||||
{frame_partial, [sequence],
|
||||
[handle_followed_packet]}].
|
||||
|
||||
init_per_suite(Config) ->
|
||||
emqx_ct_helpers:start_apps([], fun set_special_configs/1),
|
||||
MqttCaps = maps:from_list(emqx_mqtt_caps:default_caps()),
|
||||
emqx_zone:set_env(external, '$mqtt_caps', MqttCaps#{max_topic_alias => 20}),
|
||||
Config.
|
||||
|
||||
end_per_suite(_Config) ->
|
||||
emqx_ct_helpers:stop_apps([]).
|
||||
|
||||
batch_connect(NumberOfConnections) ->
|
||||
batch_connect([], NumberOfConnections).
|
||||
|
||||
batch_connect(Socks, 0) ->
|
||||
Socks;
|
||||
batch_connect(Socks, NumberOfConnections) ->
|
||||
{ok, Sock} = emqx_client_sock:connect({127, 0, 0, 1}, 1883,
|
||||
[binary, {packet, raw}, {active, false}],
|
||||
3000),
|
||||
batch_connect([Sock | Socks], NumberOfConnections - 1).
|
||||
|
||||
with_connection(DoFun, NumberOfConnections) ->
|
||||
Socks = batch_connect(NumberOfConnections),
|
||||
try
|
||||
DoFun(Socks)
|
||||
after
|
||||
lists:foreach(fun(Sock) ->
|
||||
emqx_client_sock:close(Sock)
|
||||
end, Socks)
|
||||
end.
|
||||
|
||||
with_connection(DoFun) ->
|
||||
with_connection(DoFun, 1).
|
||||
|
||||
handle_followed_packet(_Config) ->
|
||||
ConnPkt = <<16,12,0,4,77,81,84,84,4,2,0,60,0,0>>,
|
||||
PartialPkt1 = <<50,182,1,0,4,116,101,115,116,0,1,48,48,48,48,48,48,48,48,48,48,48,48,48,
|
||||
48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,
|
||||
48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,
|
||||
48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,
|
||||
48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48>>,
|
||||
PartialPkt2 = <<48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,
|
||||
48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,
|
||||
48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48>>,
|
||||
|
||||
%% This is a PUBLISH message (Qos=1)
|
||||
PubPkt = <<PartialPkt1/binary, PartialPkt2/binary>>,
|
||||
ComplexPkt = <<PubPkt/binary, PubPkt/binary, PubPkt/binary, PartialPkt1/binary>>,
|
||||
|
||||
AssertConnAck = fun(R) -> ?assertEqual({ok, <<32,2,0,0>>}, R) end,
|
||||
AssertPubAck = fun(R) -> ?assertEqual({ok, <<64,2,0,1>>}, R) end,
|
||||
|
||||
{ok, Sock} = gen_tcp:connect("127.0.0.1", 1883, [{active, false}, binary]),
|
||||
|
||||
%% CONNECT
|
||||
ok = gen_tcp:send(Sock, ConnPkt),
|
||||
AssertConnAck(gen_tcp:recv(Sock, 4, 500)),
|
||||
|
||||
%% Once Publish
|
||||
ok = gen_tcp:send(Sock, PubPkt),
|
||||
AssertPubAck(gen_tcp:recv(Sock, 4, 500)),
|
||||
|
||||
%% Complex Packet
|
||||
ok = gen_tcp:send(Sock, ComplexPkt),
|
||||
AssertPubAck(gen_tcp:recv(Sock, 4, 500)),
|
||||
AssertPubAck(gen_tcp:recv(Sock, 4, 500)),
|
||||
AssertPubAck(gen_tcp:recv(Sock, 4, 500)),
|
||||
|
||||
ok = gen_tcp:send(Sock, PartialPkt2),
|
||||
AssertPubAck(gen_tcp:recv(Sock, 4, 500)),
|
||||
gen_tcp:close(Sock).
|
||||
|
||||
connect_v4(_) ->
|
||||
with_connection(fun([Sock]) ->
|
||||
emqx_client_sock:send(Sock, raw_send_serialize(?PACKET(?PUBLISH))),
|
||||
{error, closed} =gen_tcp:recv(Sock, 0)
|
||||
end),
|
||||
with_connection(fun([Sock]) ->
|
||||
ConnectPacket = raw_send_serialize(?CONNECT_PACKET
|
||||
(#mqtt_packet_connect{
|
||||
client_id = <<"mqttv4_client">>,
|
||||
username = <<"admin">>,
|
||||
password = <<"public">>,
|
||||
proto_ver = ?MQTT_PROTO_V4
|
||||
})),
|
||||
emqx_client_sock:send(Sock, ConnectPacket),
|
||||
{ok, Data} = gen_tcp:recv(Sock, 0),
|
||||
{ok, ?CONNACK_PACKET(?CONNACK_ACCEPT), <<>>, _} = raw_recv_parse(Data, ?MQTT_PROTO_V4),
|
||||
|
||||
emqx_client_sock:send(Sock, ConnectPacket),
|
||||
{error, closed} = gen_tcp:recv(Sock, 0)
|
||||
end),
|
||||
ok.
|
||||
|
||||
|
||||
connect_v5(_) ->
|
||||
with_connection(fun([Sock]) ->
|
||||
emqx_client_sock:send(Sock,
|
||||
raw_send_serialize(
|
||||
?CONNECT_PACKET(#mqtt_packet_connect{
|
||||
proto_ver = ?MQTT_PROTO_V5,
|
||||
properties =
|
||||
#{'Request-Response-Information' => -1}}))),
|
||||
{ok, Data} = gen_tcp:recv(Sock, 0),
|
||||
{ok, ?DISCONNECT_PACKET(?RC_PROTOCOL_ERROR), <<>>, _} = raw_recv_parse(Data, ?MQTT_PROTO_V5)
|
||||
end),
|
||||
|
||||
with_connection(fun([Sock]) ->
|
||||
emqx_client_sock:send(Sock,
|
||||
raw_send_serialize(
|
||||
?CONNECT_PACKET(
|
||||
#mqtt_packet_connect{
|
||||
proto_ver = ?MQTT_PROTO_V5,
|
||||
properties =
|
||||
#{'Request-Problem-Information' => 2}}))),
|
||||
{ok, Data} = gen_tcp:recv(Sock, 0),
|
||||
{ok, ?DISCONNECT_PACKET(?RC_PROTOCOL_ERROR), <<>>, _} = raw_recv_parse(Data, ?MQTT_PROTO_V5)
|
||||
end),
|
||||
|
||||
with_connection(fun([Sock]) ->
|
||||
emqx_client_sock:send(Sock,
|
||||
raw_send_serialize(
|
||||
?CONNECT_PACKET(
|
||||
#mqtt_packet_connect{
|
||||
proto_ver = ?MQTT_PROTO_V5,
|
||||
properties =
|
||||
#{'Request-Response-Information' => 1}})
|
||||
)),
|
||||
{ok, Data} = gen_tcp:recv(Sock, 0),
|
||||
{ok, ?CONNACK_PACKET(?RC_SUCCESS, 0, Props), <<>>, _} =
|
||||
raw_recv_parse(Data, ?MQTT_PROTO_V5),
|
||||
?assertNot(maps:is_key('Response-Information', Props))
|
||||
end),
|
||||
|
||||
% topic alias = 0
|
||||
with_connection(fun([Sock]) ->
|
||||
|
||||
%% ct:log("emqx_protocol: ~p~n", [emqx_zone:get_zone(external, max_topic_alias)]),
|
||||
emqx_client_sock:send(Sock,
|
||||
raw_send_serialize(
|
||||
?CONNECT_PACKET(
|
||||
#mqtt_packet_connect{
|
||||
client_id = "hello",
|
||||
proto_ver = ?MQTT_PROTO_V5,
|
||||
properties =
|
||||
#{'Topic-Alias-Maximum' => 10}}),
|
||||
#{version => ?MQTT_PROTO_V5}
|
||||
)),
|
||||
{ok, Data} = gen_tcp:recv(Sock, 0),
|
||||
{ok, ?CONNACK_PACKET(?RC_SUCCESS, 0,
|
||||
#{'Topic-Alias-Maximum' := 20}), <<>>, _} =
|
||||
raw_recv_parse(Data, ?MQTT_PROTO_V5),
|
||||
emqx_client_sock:send(Sock,
|
||||
raw_send_serialize(
|
||||
?PUBLISH_PACKET(?QOS_1, <<"TopicA">>, 1, #{'Topic-Alias' => 0}, <<"hello">>),
|
||||
#{version => ?MQTT_PROTO_V5}
|
||||
)),
|
||||
|
||||
{ok, Data2} = gen_tcp:recv(Sock, 0),
|
||||
{ok, ?DISCONNECT_PACKET(?RC_TOPIC_ALIAS_INVALID), <<>>, _} = raw_recv_parse(Data2, ?MQTT_PROTO_V5)
|
||||
end),
|
||||
|
||||
% topic alias maximum
|
||||
with_connection(fun([Sock]) ->
|
||||
emqx_client_sock:send(Sock,
|
||||
raw_send_serialize(
|
||||
?CONNECT_PACKET(
|
||||
#mqtt_packet_connect{
|
||||
proto_ver = ?MQTT_PROTO_V5,
|
||||
properties =
|
||||
#{'Topic-Alias-Maximum' => 10}}),
|
||||
#{version => ?MQTT_PROTO_V5}
|
||||
)),
|
||||
{ok, Data} = gen_tcp:recv(Sock, 0),
|
||||
{ok, ?CONNACK_PACKET(?RC_SUCCESS, 0,
|
||||
#{'Topic-Alias-Maximum' := 20}), <<>>, _} =
|
||||
raw_recv_parse(Data, ?MQTT_PROTO_V5),
|
||||
|
||||
emqx_client_sock:send(Sock, raw_send_serialize(?SUBSCRIBE_PACKET(1, [{<<"TopicA">>, #{rh => 1,
|
||||
qos => ?QOS_2,
|
||||
rap => 0,
|
||||
nl => 0,
|
||||
rc => 0}}]), #{version => ?MQTT_PROTO_V5})),
|
||||
|
||||
{ok, Data2} = gen_tcp:recv(Sock, 0),
|
||||
{ok, ?SUBACK_PACKET(1, #{}, [2]), <<>>, _} = raw_recv_parse(Data2, ?MQTT_PROTO_V5),
|
||||
|
||||
emqx_client_sock:send(Sock,
|
||||
raw_send_serialize(
|
||||
?PUBLISH_PACKET(?QOS_1, <<"TopicA">>, 1, #{'Topic-Alias' => 15}, <<"hello">>),
|
||||
#{version => ?MQTT_PROTO_V5}
|
||||
)),
|
||||
|
||||
{ok, Data3} = gen_tcp:recv(Sock, 6),
|
||||
|
||||
{ok, ?PUBACK_PACKET(1, 0), <<>>, _} = raw_recv_parse(Data3, ?MQTT_PROTO_V5),
|
||||
|
||||
{ok, Data4} = gen_tcp:recv(Sock, 0),
|
||||
|
||||
{ok, ?PUBLISH_PACKET(?QOS_1, <<"TopicA">>, _, <<"hello">>), <<>>, _} = raw_recv_parse(Data4, ?MQTT_PROTO_V5),
|
||||
|
||||
emqx_client_sock:send(Sock,
|
||||
raw_send_serialize(
|
||||
?PUBLISH_PACKET(?QOS_1, <<"TopicA">>, 2, #{'Topic-Alias' => 21}, <<"hello">>),
|
||||
#{version => ?MQTT_PROTO_V5}
|
||||
)),
|
||||
|
||||
{ok, Data5} = gen_tcp:recv(Sock, 0),
|
||||
{ok, ?DISCONNECT_PACKET(?RC_TOPIC_ALIAS_INVALID), <<>>, _} = raw_recv_parse(Data5, ?MQTT_PROTO_V5)
|
||||
end),
|
||||
|
||||
% test clean start
|
||||
with_connection(fun([Sock]) ->
|
||||
emqx_client_sock:send(Sock,
|
||||
raw_send_serialize(
|
||||
?CONNECT_PACKET(
|
||||
#mqtt_packet_connect{
|
||||
proto_ver = ?MQTT_PROTO_V5,
|
||||
clean_start = true,
|
||||
client_id = <<"myclient">>,
|
||||
properties =
|
||||
#{'Session-Expiry-Interval' => 10}})
|
||||
)),
|
||||
{ok, Data} = gen_tcp:recv(Sock, 0),
|
||||
{ok, ?CONNACK_PACKET(?RC_SUCCESS, 0), <<>>, _} = raw_recv_parse(Data, ?MQTT_PROTO_V5),
|
||||
emqx_client_sock:send(Sock, raw_send_serialize(
|
||||
?DISCONNECT_PACKET(?RC_SUCCESS)
|
||||
))
|
||||
end),
|
||||
|
||||
timer:sleep(1000),
|
||||
|
||||
with_connection(fun([Sock]) ->
|
||||
emqx_client_sock:send(Sock,
|
||||
raw_send_serialize(
|
||||
?CONNECT_PACKET(
|
||||
#mqtt_packet_connect{
|
||||
proto_ver = ?MQTT_PROTO_V5,
|
||||
clean_start = false,
|
||||
client_id = <<"myclient">>,
|
||||
properties =
|
||||
#{'Session-Expiry-Interval' => 10}})
|
||||
)),
|
||||
{ok, Data} = gen_tcp:recv(Sock, 0),
|
||||
{ok, ?CONNACK_PACKET(?RC_SUCCESS, 1), <<>>, _} = raw_recv_parse(Data, ?MQTT_PROTO_V5)
|
||||
end),
|
||||
|
||||
% test will message publish and cancel
|
||||
with_connection(fun([Sock]) ->
|
||||
emqx_client_sock:send(Sock,
|
||||
raw_send_serialize(
|
||||
?CONNECT_PACKET(
|
||||
#mqtt_packet_connect{
|
||||
proto_ver = ?MQTT_PROTO_V5,
|
||||
clean_start = true,
|
||||
client_id = <<"myclient">>,
|
||||
will_flag = true,
|
||||
will_qos = ?QOS_1,
|
||||
will_retain = false,
|
||||
will_props = #{'Will-Delay-Interval' => 5},
|
||||
will_topic = <<"TopicA">>,
|
||||
will_payload = <<"will message">>,
|
||||
properties = #{'Session-Expiry-Interval' => 0}
|
||||
}
|
||||
)
|
||||
)
|
||||
),
|
||||
{ok, Data} = gen_tcp:recv(Sock, 0),
|
||||
{ok, ?CONNACK_PACKET(?RC_SUCCESS, 0), <<>>, _} = raw_recv_parse(Data, ?MQTT_PROTO_V5),
|
||||
|
||||
{ok, Sock2} = emqx_client_sock:connect({127, 0, 0, 1}, 1883,
|
||||
[binary, {packet, raw},
|
||||
{active, false}], 3000),
|
||||
|
||||
do_connect(Sock2, ?MQTT_PROTO_V5),
|
||||
|
||||
emqx_client_sock:send(Sock2, raw_send_serialize(?SUBSCRIBE_PACKET(1, [{<<"TopicA">>, #{rh => 1,
|
||||
qos => ?QOS_2,
|
||||
rap => 0,
|
||||
nl => 0,
|
||||
rc => 0}}]), #{version => ?MQTT_PROTO_V5})),
|
||||
|
||||
{ok, SubData} = gen_tcp:recv(Sock2, 0),
|
||||
{ok, ?SUBACK_PACKET(1, #{}, [2]), <<>>, _} = raw_recv_parse(SubData, ?MQTT_PROTO_V5),
|
||||
|
||||
emqx_client_sock:send(Sock, raw_send_serialize(
|
||||
?DISCONNECT_PACKET(?RC_SUCCESS))),
|
||||
|
||||
{error, timeout} = gen_tcp:recv(Sock2, 0, 2000),
|
||||
|
||||
% session resumed
|
||||
{ok, Sock3} = emqx_client_sock:connect({127, 0, 0, 1}, 1883,
|
||||
[binary, {packet, raw},
|
||||
{active, false}], 3000),
|
||||
|
||||
emqx_client_sock:send(Sock3,
|
||||
raw_send_serialize(
|
||||
?CONNECT_PACKET(
|
||||
#mqtt_packet_connect{
|
||||
proto_ver = ?MQTT_PROTO_V5,
|
||||
clean_start = false,
|
||||
client_id = <<"myclient">>,
|
||||
will_flag = true,
|
||||
will_qos = ?QOS_1,
|
||||
will_retain = false,
|
||||
will_props = #{'Will-Delay-Interval' => 5},
|
||||
will_topic = <<"TopicA">>,
|
||||
will_payload = <<"will message 2">>,
|
||||
properties = #{'Session-Expiry-Interval' => 3}
|
||||
}
|
||||
),
|
||||
#{version => ?MQTT_PROTO_V5}
|
||||
)
|
||||
),
|
||||
{ok, Data3} = gen_tcp:recv(Sock3, 0),
|
||||
{ok, ?CONNACK_PACKET(?RC_SUCCESS, 0), <<>>, _} = raw_recv_parse(Data3, ?MQTT_PROTO_V5),
|
||||
|
||||
emqx_client_sock:send(Sock3, raw_send_serialize(
|
||||
?DISCONNECT_PACKET(?RC_DISCONNECT_WITH_WILL_MESSAGE),
|
||||
#{version => ?MQTT_PROTO_V5}
|
||||
)
|
||||
),
|
||||
|
||||
{ok, WillData} = gen_tcp:recv(Sock2, 0, 5000),
|
||||
{ok, ?PUBLISH_PACKET(?QOS_1, <<"TopicA">>, _, <<"will message 2">>), <<>>, _}
|
||||
= raw_recv_parse(WillData, ?MQTT_PROTO_V5)
|
||||
end),
|
||||
|
||||
% duplicate client id
|
||||
with_connection(fun([Sock, Sock1]) ->
|
||||
emqx_zone:set_env(external, use_username_as_clientid, true),
|
||||
emqx_client_sock:send(Sock,
|
||||
raw_send_serialize(
|
||||
?CONNECT_PACKET(
|
||||
#mqtt_packet_connect{
|
||||
proto_ver = ?MQTT_PROTO_V5,
|
||||
clean_start = true,
|
||||
client_id = <<"myclient">>,
|
||||
properties =
|
||||
#{'Session-Expiry-Interval' => 10}})
|
||||
)),
|
||||
{ok, Data} = gen_tcp:recv(Sock, 0),
|
||||
{ok, ?CONNACK_PACKET(?RC_SUCCESS, 0), <<>>, _} = raw_recv_parse(Data, ?MQTT_PROTO_V5),
|
||||
|
||||
emqx_client_sock:send(Sock1,
|
||||
raw_send_serialize(
|
||||
?CONNECT_PACKET(
|
||||
#mqtt_packet_connect{
|
||||
proto_ver = ?MQTT_PROTO_V5,
|
||||
clean_start = false,
|
||||
client_id = <<"myclient">>,
|
||||
username = <<"admin">>,
|
||||
password = <<"public">>,
|
||||
properties =
|
||||
#{'Session-Expiry-Interval' => 10}})
|
||||
)),
|
||||
{ok, Data1} = gen_tcp:recv(Sock1, 0),
|
||||
{ok, ?CONNACK_PACKET(?RC_SUCCESS, 0), <<>>, _} = raw_recv_parse(Data1, ?MQTT_PROTO_V5),
|
||||
|
||||
emqx_client_sock:send(Sock, raw_send_serialize(?SUBSCRIBE_PACKET(1, [{<<"TopicA">>, #{rh => 1,
|
||||
qos => ?QOS_2,
|
||||
rap => 0,
|
||||
nl => 0,
|
||||
rc => 0}}]),
|
||||
#{version => ?MQTT_PROTO_V5})),
|
||||
|
||||
{ok, SubData} = gen_tcp:recv(Sock, 0),
|
||||
{ok, ?SUBACK_PACKET(1, #{}, [2]), <<>>, _} = raw_recv_parse(SubData, ?MQTT_PROTO_V5),
|
||||
|
||||
emqx_client_sock:send(Sock1, raw_send_serialize(?SUBSCRIBE_PACKET(1, [{<<"TopicA">>, #{rh => 1,
|
||||
qos => ?QOS_2,
|
||||
rap => 0,
|
||||
nl => 0,
|
||||
rc => 0}}]),
|
||||
#{version => ?MQTT_PROTO_V5})),
|
||||
|
||||
{ok, SubData1} = gen_tcp:recv(Sock1, 0),
|
||||
{ok, ?SUBACK_PACKET(1, #{}, [2]), <<>>, _} = raw_recv_parse(SubData1, ?MQTT_PROTO_V5)
|
||||
end, 2),
|
||||
|
||||
ok.
|
||||
|
||||
do_connect(Sock, ProtoVer) ->
|
||||
emqx_client_sock:send(Sock, raw_send_serialize(
|
||||
?CONNECT_PACKET(
|
||||
#mqtt_packet_connect{
|
||||
client_id = <<"mqtt_client">>,
|
||||
proto_ver = ProtoVer
|
||||
}))),
|
||||
{ok, Data} = gen_tcp:recv(Sock, 0),
|
||||
{ok, ?CONNACK_PACKET(?CONNACK_ACCEPT), <<>>, _} = raw_recv_parse(Data, ProtoVer).
|
||||
|
||||
subscribe_v4(_) ->
|
||||
with_connection(fun([Sock]) ->
|
||||
do_connect(Sock, ?MQTT_PROTO_V4),
|
||||
SubPacket = raw_send_serialize(
|
||||
?SUBSCRIBE_PACKET(15,
|
||||
[{<<"topic">>, #{rh => 1,
|
||||
qos => ?QOS_2,
|
||||
rap => 0,
|
||||
nl => 0,
|
||||
rc => 0}}])),
|
||||
emqx_client_sock:send(Sock, SubPacket),
|
||||
{ok, Data} = gen_tcp:recv(Sock, 0),
|
||||
{ok, ?SUBACK_PACKET(15, _), <<>>, _} = raw_recv_parse(Data, ?MQTT_PROTO_V4)
|
||||
end),
|
||||
ok.
|
||||
|
||||
subscribe_v5(_) ->
|
||||
with_connection(fun([Sock]) ->
|
||||
do_connect(Sock, ?MQTT_PROTO_V5),
|
||||
SubPacket = raw_send_serialize(?SUBSCRIBE_PACKET(15, #{'Subscription-Identifier' => -1},[]),
|
||||
#{version => ?MQTT_PROTO_V5}),
|
||||
emqx_client_sock:send(Sock, SubPacket),
|
||||
{ok, DisConnData} = gen_tcp:recv(Sock, 0),
|
||||
{ok, ?DISCONNECT_PACKET(?RC_TOPIC_FILTER_INVALID), <<>>, _} =
|
||||
raw_recv_parse(DisConnData, ?MQTT_PROTO_V5)
|
||||
end),
|
||||
with_connection(fun([Sock]) ->
|
||||
do_connect(Sock, ?MQTT_PROTO_V5),
|
||||
SubPacket = raw_send_serialize(
|
||||
?SUBSCRIBE_PACKET(0, #{}, [{<<"TopicQos0">>,
|
||||
#{rh => 1, qos => ?QOS_2,
|
||||
rap => 0, nl => 0,
|
||||
rc => 0}}]),
|
||||
#{version => ?MQTT_PROTO_V5}),
|
||||
emqx_client_sock:send(Sock, SubPacket),
|
||||
{ok, DisConnData} = gen_tcp:recv(Sock, 0),
|
||||
?assertMatch(
|
||||
{ok, ?DISCONNECT_PACKET(?RC_MALFORMED_PACKET), <<>>, _},
|
||||
raw_recv_parse(DisConnData, ?MQTT_PROTO_V5))
|
||||
end),
|
||||
with_connection(fun([Sock]) ->
|
||||
do_connect(Sock, ?MQTT_PROTO_V5),
|
||||
SubPacket = raw_send_serialize(
|
||||
?SUBSCRIBE_PACKET(1, #{'Subscription-Identifier' => 0},
|
||||
[{<<"TopicQos0">>,
|
||||
#{rh => 1, qos => ?QOS_2,
|
||||
rap => 0, nl => 0,
|
||||
rc => 0}}]),
|
||||
#{version => ?MQTT_PROTO_V5}),
|
||||
emqx_client_sock:send(Sock, SubPacket),
|
||||
{ok, DisConnData} = gen_tcp:recv(Sock, 0),
|
||||
?assertMatch(
|
||||
{ok, ?DISCONNECT_PACKET(?RC_SUBSCRIPTION_IDENTIFIERS_NOT_SUPPORTED), <<>>, _},
|
||||
raw_recv_parse(DisConnData, ?MQTT_PROTO_V5))
|
||||
end),
|
||||
with_connection(fun([Sock]) ->
|
||||
do_connect(Sock, ?MQTT_PROTO_V5),
|
||||
SubPacket = raw_send_serialize(
|
||||
?SUBSCRIBE_PACKET(1, #{'Subscription-Identifier' => 1},
|
||||
[{<<"TopicQos0">>,
|
||||
#{rh => 1, qos => ?QOS_2,
|
||||
rap => 0, nl => 0,
|
||||
rc => 0}}]),
|
||||
#{version => ?MQTT_PROTO_V5}),
|
||||
emqx_client_sock:send(Sock, SubPacket),
|
||||
{ok, SubData} = gen_tcp:recv(Sock, 0),
|
||||
{ok, ?SUBACK_PACKET(1, #{}, [2]), <<>>, _}
|
||||
= raw_recv_parse(SubData, ?MQTT_PROTO_V5)
|
||||
end),
|
||||
ok.
|
||||
|
||||
publish_v4(_) ->
|
||||
ok.
|
||||
|
||||
publish_v5(_) ->
|
||||
ok.
|
||||
|
||||
raw_send_serialize(Packet) ->
|
||||
emqx_frame:serialize(Packet).
|
||||
|
||||
raw_send_serialize(Packet, Opts) ->
|
||||
emqx_frame:serialize(Packet, Opts).
|
||||
|
||||
raw_recv_parse(Bin, ProtoVer) ->
|
||||
emqx_frame:parse(Bin, emqx_frame:initial_parse_state(#{version => ProtoVer})).
|
||||
|
||||
acl_deny_action_ct(_) ->
|
||||
emqx_zone:set_env(external, acl_deny_action, disconnect),
|
||||
process_flag(trap_exit, true),
|
||||
[acl_deny_do_disconnect(subscribe, QoS, <<"acl_deny_action">>) || QoS <- lists:seq(0, 2)],
|
||||
[acl_deny_do_disconnect(publish, QoS, <<"acl_deny_action">>) || QoS <- lists:seq(0, 2)],
|
||||
emqx_zone:set_env(external, acl_deny_action, ignore),
|
||||
ok.
|
||||
|
||||
will_topic_check(_) ->
|
||||
{ok, Client} = emqx_client:start_link([{username, <<"emqx">>},
|
||||
{will_flag, true},
|
||||
{will_topic, <<"aaa">>},
|
||||
{will_payload, <<"I have died">>},
|
||||
{will_qos, 0}]),
|
||||
{ok, _} = emqx_client:connect(Client),
|
||||
|
||||
{ok, T} = emqx_client:start_link([{client_id, <<"client">>}]),
|
||||
emqx_client:connect(T),
|
||||
emqx_client:subscribe(T, <<"aaa">>),
|
||||
ct:sleep(200),
|
||||
|
||||
emqx_client:stop(Client),
|
||||
ct:sleep(100),
|
||||
false = is_process_alive(Client),
|
||||
emqx_ct_helpers:wait_mqtt_payload(<<"I have died">>),
|
||||
emqx_client:stop(T).
|
||||
|
||||
will_acl_check(_) ->
|
||||
%% The connection will be rejected if publishing of the will message is not allowed by
|
||||
%% ACL rules
|
||||
process_flag(trap_exit, true),
|
||||
{ok, Client} = emqx_client:start_link([{username, <<"pub_deny">>},
|
||||
{will_flag, true},
|
||||
{will_topic, <<"pub_deny">>},
|
||||
{will_payload, <<"I have died">>},
|
||||
{will_qos, 0}]),
|
||||
?assertMatch({error,{_,_}}, emqx_client:connect(Client)).
|
||||
|
||||
acl_deny_do_disconnect(publish, QoS, Topic) ->
|
||||
process_flag(trap_exit, true),
|
||||
{ok, Client} = emqx_client:start_link([{username, <<"emqx">>}]),
|
||||
{ok, _} = emqx_client:connect(Client),
|
||||
emqx_client:publish(Client, Topic, <<"test">>, QoS),
|
||||
receive
|
||||
{disconnected, shutdown, tcp_closed} ->
|
||||
ct:pal(info, "[OK] after publish, client got disconnected: tcp_closed", []);
|
||||
{'EXIT', Client, {shutdown,tcp_closed}} ->
|
||||
ct:pal(info, "[OK] after publish, received exit: {shutdown,tcp_closed}"),
|
||||
false = is_process_alive(Client);
|
||||
{'EXIT', Client, Reason} ->
|
||||
ct:pal(info, "[OK] after publish, client got disconnected: ~p", [Reason])
|
||||
after 1000 -> ct:fail({timeout, wait_tcp_closed})
|
||||
end;
|
||||
|
||||
acl_deny_do_disconnect(subscribe, QoS, Topic) ->
|
||||
process_flag(trap_exit, true),
|
||||
{ok, Client} = emqx_client:start_link([{username, <<"emqx">>}]),
|
||||
{ok, _} = emqx_client:connect(Client),
|
||||
{ok, _, [128]} = emqx_client:subscribe(Client, Topic, QoS),
|
||||
receive
|
||||
{disconnected, shutdown, tcp_closed} ->
|
||||
ct:pal(info, "[OK] after subscribe, client got disconnected: tcp_closed", []);
|
||||
{'EXIT', Client, {shutdown,tcp_closed}} ->
|
||||
ct:pal(info, "[OK] after subscribe, received exit: {shutdown,tcp_closed}"),
|
||||
false = is_process_alive(Client);
|
||||
{'EXIT', Client, Reason} ->
|
||||
ct:pal(info, "[OK] after subscribe, client got disconnected: ~p", [Reason])
|
||||
after 1000 -> ct:fail({timeout, wait_tcp_closed})
|
||||
end.
|
||||
|
||||
set_special_configs(emqx) ->
|
||||
application:set_env(emqx, enable_acl_cache, false),
|
||||
application:set_env(emqx, plugins_loaded_file,
|
||||
emqx_ct_helpers:deps_path(emqx, "test/emqx_SUITE_data/loaded_plugins")),
|
||||
application:set_env(emqx, acl_deny_action, disconnect),
|
||||
application:set_env(emqx, acl_file,
|
||||
emqx_ct_helpers:deps_path(emqx, "test/emqx_access_SUITE_data/acl_deny_action.conf"));
|
||||
set_special_configs(_App) ->
|
||||
ok.
|
|
@ -1,30 +0,0 @@
|
|||
%% Copyright (c) 2013-2019 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_protocol_tests).
|
||||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
set_property_test() ->
|
||||
?assertEqual(#{test => test_property}, emqx_protocol:set_property(test, test_property, undefined)),
|
||||
TestMap = #{test => test_property},
|
||||
?assertEqual(#{test => test_property, test1 => test_property2},
|
||||
emqx_protocol:set_property(test1, test_property2, TestMap)),
|
||||
ok.
|
||||
|
||||
init_username_test() ->
|
||||
?assertEqual(<<"Peercert">>,
|
||||
emqx_protocol:init_username(<<"Peercert">>, [{peer_cert_as_username, crt}])),
|
||||
?assertEqual(undefined,
|
||||
emqx_protocol:init_username(undefined, [{peer_cert_as_username, undefined}])).
|
|
@ -14,12 +14,13 @@
|
|||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
-module(emqx_reason_codes_SUITE).
|
||||
|
||||
-module(emqx_reason_codes_tests).
|
||||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
-compile(export_all).
|
||||
-compile(nowarn_export_all).
|
||||
|
||||
-include("emqx_mqtt.hrl").
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
-import(lists, [seq/2, zip/2, foreach/2]).
|
||||
|
||||
|
@ -89,25 +90,27 @@
|
|||
?CONNACK_AUTH,
|
||||
?CONNACK_SERVER, ?CONNACK_SERVER, ?CONNACK_SERVER, ?CONNACK_SERVER]).
|
||||
|
||||
mqttv4_name_test() ->
|
||||
all() -> emqx_ct:all(?MODULE).
|
||||
|
||||
t_mqttv4_name() ->
|
||||
(((codes_test(?MQTT_PROTO_V4))
|
||||
(seq(0,6)))
|
||||
(?MQTTV4_CODE_NAMES))
|
||||
(fun emqx_reason_codes:name/2).
|
||||
|
||||
mqttv5_name_test() ->
|
||||
t_mqttv5_name() ->
|
||||
(((codes_test(?MQTT_PROTO_V5))
|
||||
(?MQTTV5_CODES))
|
||||
(?MQTTV5_CODE_NAMES))
|
||||
(fun emqx_reason_codes:name/2).
|
||||
|
||||
text_test() ->
|
||||
t_text() ->
|
||||
(((codes_test(?MQTT_PROTO_V5))
|
||||
(?MQTTV5_CODES))
|
||||
(?MQTTV5_TXT))
|
||||
(fun emqx_reason_codes:text/1).
|
||||
|
||||
compat_test() ->
|
||||
t_compat() ->
|
||||
(((codes_test(connack))
|
||||
(?COMPAT_CODES_V5))
|
||||
(?COMPAT_CODES_V4))
|
||||
|
@ -135,3 +138,4 @@ codes_test(AsistVar) ->
|
|||
end
|
||||
end
|
||||
end.
|
||||
|
|
@ -1,97 +0,0 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2019 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.
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
%% @doc This module implements a request handler based on emqx_client.
|
||||
%% A request handler is a MQTT client which subscribes to a request topic,
|
||||
%% processes the requests then send response to another topic which is
|
||||
%% subscribed by the request sender.
|
||||
%% This code is in test directory because request and response are pure
|
||||
%% client-side behaviours.
|
||||
|
||||
-module(emqx_request_handler).
|
||||
|
||||
-export([start_link/4, stop/1]).
|
||||
|
||||
-type qos() :: emqx_mqtt_types:qos_name() | emqx_mqtt_types:qos().
|
||||
-type topic() :: emqx_topic:topic().
|
||||
-type handler() :: fun((CorrData :: binary(), ReqPayload :: binary()) -> RspPayload :: binary()).
|
||||
|
||||
-spec start_link(topic(), qos(), handler(), emqx_client:options()) ->
|
||||
{ok, pid()} | {error, any()}.
|
||||
start_link(RequestTopic, QoS, RequestHandler, Options0) ->
|
||||
Parent = self(),
|
||||
MsgHandler = make_msg_handler(RequestHandler, Parent),
|
||||
Options = [{msg_handler, MsgHandler} | Options0],
|
||||
case emqx_client:start_link(Options) of
|
||||
{ok, Pid} ->
|
||||
{ok, _} = emqx_client:connect(Pid),
|
||||
try subscribe(Pid, RequestTopic, QoS) of
|
||||
ok -> {ok, Pid};
|
||||
{error, _} = Error -> Error
|
||||
catch
|
||||
C : E : S ->
|
||||
emqx_client:stop(Pid),
|
||||
{error, {C, E, S}}
|
||||
end;
|
||||
{error, _} = Error -> Error
|
||||
end.
|
||||
|
||||
stop(Pid) ->
|
||||
emqx_client:disconnect(Pid).
|
||||
|
||||
make_msg_handler(RequestHandler, Parent) ->
|
||||
#{publish => fun(Msg) -> handle_msg(Msg, RequestHandler, Parent) end,
|
||||
puback => fun(_Ack) -> ok end,
|
||||
disconnected => fun(_Reason) -> ok end
|
||||
}.
|
||||
|
||||
handle_msg(ReqMsg, RequestHandler, Parent) ->
|
||||
#{qos := QoS, properties := Props, payload := ReqPayload} = ReqMsg,
|
||||
case maps:find('Response-Topic', Props) of
|
||||
{ok, RspTopic} when RspTopic =/= <<>> ->
|
||||
CorrData = maps:get('Correlation-Data', Props),
|
||||
RspProps = maps:without(['Response-Topic'], Props),
|
||||
RspPayload = RequestHandler(CorrData, ReqPayload),
|
||||
emqx_logger:debug("~p sending response msg to topic ~s with~n"
|
||||
"corr-data=~p~npayload=~p",
|
||||
[?MODULE, RspTopic, CorrData, RspPayload]),
|
||||
ok = send_response(RspTopic, RspProps, RspPayload, QoS);
|
||||
_ ->
|
||||
Parent ! {discarded, ReqPayload},
|
||||
ok
|
||||
end.
|
||||
|
||||
send_response(Topic, Properties, Payload, QoS) ->
|
||||
%% This function is evaluated by emqx_client itself.
|
||||
%% hence delegate to another temp process for the loopback gen_statem call.
|
||||
Client = self(),
|
||||
_ = spawn_link(fun() ->
|
||||
case emqx_client:publish(Client, Topic, Properties, Payload, [{qos, QoS}]) of
|
||||
ok -> ok;
|
||||
{ok, _} -> ok;
|
||||
{error, Reason} -> exit({failed_to_publish_response, Reason})
|
||||
end
|
||||
end),
|
||||
ok.
|
||||
|
||||
subscribe(Client, Topic, QoS) ->
|
||||
{ok, _Props, _QoS} =
|
||||
emqx_client:subscribe(Client, [{Topic, [{rh, 2}, {rap, false},
|
||||
{nl, true}, {qos, QoS}]}]),
|
||||
ok.
|
||||
|
||||
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2019 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_request_response_SUITE).
|
||||
|
||||
-compile(export_all).
|
||||
-compile(nowarn_export_all).
|
||||
|
||||
-include("emqx_mqtt.hrl").
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
-include_lib("common_test/include/ct.hrl").
|
||||
|
||||
init_per_suite(Config) ->
|
||||
emqx_ct_helpers:start_apps([]),
|
||||
Config.
|
||||
|
||||
end_per_suite(_Config) ->
|
||||
emqx_ct_helpers:stop_apps([]).
|
||||
|
||||
all() ->
|
||||
[request_response].
|
||||
|
||||
request_response(_Config) ->
|
||||
request_response_per_qos(?QOS_0),
|
||||
request_response_per_qos(?QOS_1),
|
||||
request_response_per_qos(?QOS_2).
|
||||
|
||||
request_response_per_qos(QoS) ->
|
||||
ReqTopic = <<"request_topic">>,
|
||||
RspTopic = <<"response_topic">>,
|
||||
{ok, Requester} = emqx_request_sender:start_link(RspTopic, QoS,
|
||||
[{proto_ver, v5},
|
||||
{client_id, <<"requester">>},
|
||||
{properties, #{ 'Request-Response-Information' => 1}}]),
|
||||
%% This is a square service
|
||||
Square = fun(_CorrData, ReqBin) ->
|
||||
I = b2i(ReqBin),
|
||||
i2b(I * I)
|
||||
end,
|
||||
{ok, Responser} = emqx_request_handler:start_link(ReqTopic, QoS, Square,
|
||||
[{proto_ver, v5},
|
||||
{client_id, <<"responser">>}
|
||||
]),
|
||||
ok = emqx_request_sender:send(Requester, ReqTopic, RspTopic, <<"corr-1">>, <<"2">>, QoS),
|
||||
receive
|
||||
{response, <<"corr-1">>, <<"4">>} ->
|
||||
ok;
|
||||
Other ->
|
||||
erlang:error({unexpected, Other})
|
||||
after
|
||||
100 ->
|
||||
erlang:error(timeout)
|
||||
end,
|
||||
ok = emqx_request_sender:stop(Requester),
|
||||
ok = emqx_request_handler:stop(Responser).
|
||||
|
||||
b2i(B) -> binary_to_integer(B).
|
||||
i2b(I) -> integer_to_binary(I).
|
|
@ -1,77 +0,0 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2019 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.
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
%% @doc This module implements a request sender based on emqx_client.
|
||||
%% A request sender is a MQTT client which sends messages to a request
|
||||
%% topic, and subscribes to another topic for responses.
|
||||
%% This code is in test directory because request and response are pure
|
||||
%% client-side behaviours.
|
||||
|
||||
-module(emqx_request_sender).
|
||||
|
||||
-export([start_link/3, stop/1, send/6]).
|
||||
|
||||
start_link(ResponseTopic, QoS, Options0) ->
|
||||
Parent = self(),
|
||||
MsgHandler = make_msg_handler(Parent),
|
||||
Options = [{msg_handler, MsgHandler} | Options0],
|
||||
case emqx_client:start_link(Options) of
|
||||
{ok, Pid} ->
|
||||
{ok, _} = emqx_client:connect(Pid),
|
||||
try subscribe(Pid, ResponseTopic, QoS) of
|
||||
ok -> {ok, Pid};
|
||||
{error, _} = Error -> Error
|
||||
catch
|
||||
C : E : S ->
|
||||
emqx_client:stop(Pid),
|
||||
{error, {C, E, S}}
|
||||
end;
|
||||
{error, _} = Error -> Error
|
||||
end.
|
||||
|
||||
%% @doc Send a message to request topic with correlation-data `CorrData'.
|
||||
%% Response should be delivered as a `{response, CorrData, Payload}'
|
||||
send(Client, ReqTopic, RspTopic, CorrData, Payload, QoS) ->
|
||||
Props = #{'Response-Topic' => RspTopic,
|
||||
'Correlation-Data' => CorrData
|
||||
},
|
||||
case emqx_client:publish(Client, ReqTopic, Props, Payload, [{qos, QoS}]) of
|
||||
ok -> ok; %% QoS = 0
|
||||
{ok, _} -> ok;
|
||||
{error, _} = E -> E
|
||||
end.
|
||||
|
||||
stop(Pid) ->
|
||||
emqx_client:disconnect(Pid).
|
||||
|
||||
subscribe(Client, Topic, QoS) ->
|
||||
case emqx_client:subscribe(Client, Topic, QoS) of
|
||||
{ok, _, _} -> ok;
|
||||
{error, _} = Error -> Error
|
||||
end.
|
||||
|
||||
make_msg_handler(Parent) ->
|
||||
#{publish => fun(Msg) -> handle_msg(Msg, Parent) end,
|
||||
puback => fun(_Ack) -> ok end,
|
||||
disconnected => fun(_Reason) -> ok end
|
||||
}.
|
||||
|
||||
handle_msg(Msg, Parent) ->
|
||||
#{properties := Props, payload := Payload} = Msg,
|
||||
CorrData = maps:get('Correlation-Data', Props),
|
||||
Parent ! {response, CorrData, Payload},
|
||||
ok.
|
||||
|
|
@ -16,26 +16,15 @@
|
|||
|
||||
-module(emqx_router_SUITE).
|
||||
|
||||
-include("emqx.hrl").
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
-compile(export_all).
|
||||
-compile(nowarn_export_all).
|
||||
|
||||
-include("emqx.hrl").
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
-define(R, emqx_router).
|
||||
|
||||
all() ->
|
||||
[{group, route}].
|
||||
|
||||
groups() ->
|
||||
[{route, [sequence],
|
||||
[t_mnesia,
|
||||
t_add_delete,
|
||||
t_do_add_delete,
|
||||
t_match_routes,
|
||||
t_print_routes,
|
||||
t_has_routes,
|
||||
t_unexpected]}].
|
||||
all() -> emqx_ct:all(?MODULE).
|
||||
|
||||
init_per_suite(Config) ->
|
||||
emqx_ct_helpers:start_apps([]),
|
||||
|
@ -107,4 +96,6 @@ t_unexpected(_) ->
|
|||
Router ! bad_info.
|
||||
|
||||
clear_tables() ->
|
||||
lists:foreach(fun mnesia:clear_table/1, [emqx_route, emqx_trie, emqx_trie_node]).
|
||||
lists:foreach(fun mnesia:clear_table/1,
|
||||
[emqx_route, emqx_trie, emqx_trie_node]).
|
||||
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2019 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_rpc_SUITE).
|
||||
|
||||
-include("emqx.hrl").
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
-compile(export_all).
|
||||
-compile(nowarn_export_all).
|
||||
-define(MASTER, 'emqxct@127.0.0.1').
|
||||
|
||||
all() -> [t_rpc].
|
||||
|
||||
init_per_suite(Config) ->
|
||||
emqx_ct_helpers:start_apps([]),
|
||||
Config.
|
||||
|
||||
end_per_suite(_Config) ->
|
||||
emqx_ct_helpers:stop_apps([]).
|
||||
|
||||
t_rpc(_) ->
|
||||
60000 = emqx_rpc:call(?MASTER, timer, seconds, [60]),
|
||||
{badrpc, _} = emqx_rpc:call(?MASTER, os, test, []),
|
||||
{_, []} = emqx_rpc:multicall([?MASTER, ?MASTER], os, timestamp, []).
|
|
@ -21,20 +21,29 @@
|
|||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
-import(emqx_sequence, [nextval/2, reclaim/2]).
|
||||
-import(emqx_sequence,
|
||||
[ nextval/2
|
||||
, currval/2
|
||||
, reclaim/2
|
||||
]).
|
||||
|
||||
all() ->
|
||||
[sequence_generate].
|
||||
all() -> emqx_ct:all(?MODULE).
|
||||
|
||||
sequence_generate(_) ->
|
||||
t_generate(_) ->
|
||||
ok = emqx_sequence:create(seqtab),
|
||||
?assertEqual(0, currval(seqtab, key)),
|
||||
?assertEqual(1, nextval(seqtab, key)),
|
||||
?assertEqual(1, currval(seqtab, key)),
|
||||
?assertEqual(2, nextval(seqtab, key)),
|
||||
?assertEqual(2, currval(seqtab, key)),
|
||||
?assertEqual(3, nextval(seqtab, key)),
|
||||
?assertEqual(2, reclaim(seqtab, key)),
|
||||
?assertEqual(1, reclaim(seqtab, key)),
|
||||
?assertEqual(0, reclaim(seqtab, key)),
|
||||
?assertEqual(false, ets:member(seqtab, key)),
|
||||
?assertEqual(1, nextval(seqtab, key)),
|
||||
?assert(emqx_sequence:delete(seqtab)).
|
||||
?assertEqual(0, reclaim(seqtab, key)),
|
||||
?assertEqual(0, reclaim(seqtab, key)),
|
||||
?assertEqual(false, ets:member(seqtab, key)),
|
||||
?assert(emqx_sequence:delete(seqtab)),
|
||||
?assertNot(emqx_sequence:delete(seqtab)).
|
||||
|
||||
|
|
|
@ -21,9 +21,7 @@
|
|||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
-include_lib("common_test/include/ct.hrl").
|
||||
|
||||
all() -> [ignore_loop, t_session_all].
|
||||
all() -> emqx_ct:all(?MODULE).
|
||||
|
||||
init_per_suite(Config) ->
|
||||
emqx_ct_helpers:start_apps([]),
|
||||
|
@ -32,6 +30,42 @@ init_per_suite(Config) ->
|
|||
end_per_suite(_Config) ->
|
||||
emqx_ct_helpers:stop_apps([]).
|
||||
|
||||
t_info(_) ->
|
||||
'TODO'.
|
||||
|
||||
t_attrs(_) ->
|
||||
'TODO'.
|
||||
|
||||
t_stats(_) ->
|
||||
'TODO'.
|
||||
|
||||
t_subscribe(_) ->
|
||||
'TODO'.
|
||||
|
||||
t_unsubscribe(_) ->
|
||||
'TODO'.
|
||||
|
||||
t_publish(_) ->
|
||||
'TODO'.
|
||||
|
||||
t_puback(_) ->
|
||||
'TODO'.
|
||||
|
||||
t_pubrec(_) ->
|
||||
'TODO'.
|
||||
|
||||
t_pubrel(_) ->
|
||||
'TODO'.
|
||||
|
||||
t_pubcomp(_) ->
|
||||
'TODO'.
|
||||
|
||||
t_deliver(_) ->
|
||||
'TODO'.
|
||||
|
||||
t_timeout(_) ->
|
||||
'TODO'.
|
||||
|
||||
ignore_loop(_Config) ->
|
||||
emqx_zone:set_env(external, ignore_loop_deliver, true),
|
||||
{ok, Client} = emqx_client:start_link(),
|
||||
|
@ -45,7 +79,7 @@ ignore_loop(_Config) ->
|
|||
ok = emqx_client:disconnect(Client),
|
||||
emqx_zone:set_env(external, ignore_loop_deliver, false).
|
||||
|
||||
t_session_all(_) ->
|
||||
session_all(_) ->
|
||||
emqx_zone:set_env(internal, idle_timeout, 1000),
|
||||
ClientId = <<"ClientId">>,
|
||||
{ok, ConnPid} = emqx_mock_client:start_link(ClientId),
|
||||
|
@ -68,3 +102,4 @@ t_session_all(_) ->
|
|||
timer:sleep(200),
|
||||
[] = emqx:subscriptions(SPid),
|
||||
emqx_mock_client:close_session(ConnPid).
|
||||
|
||||
|
|
|
@ -1,260 +0,0 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2019 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_shared_sub_SUITE).
|
||||
|
||||
-export([all/0, init_per_suite/1, end_per_suite/1]).
|
||||
-export([t_random_basic/1,
|
||||
t_random/1,
|
||||
t_round_robin/1,
|
||||
t_sticky/1,
|
||||
t_hash/1,
|
||||
t_not_so_sticky/1,
|
||||
t_no_connection_nack/1
|
||||
]).
|
||||
|
||||
-include("emqx.hrl").
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
-include_lib("common_test/include/ct.hrl").
|
||||
|
||||
-define(wait(For, Timeout), emqx_ct_helpers:wait_for(?FUNCTION_NAME, ?LINE, fun() -> For end, Timeout)).
|
||||
|
||||
all() -> [t_random_basic,
|
||||
t_random,
|
||||
t_round_robin,
|
||||
t_sticky,
|
||||
t_hash,
|
||||
t_not_so_sticky,
|
||||
t_no_connection_nack].
|
||||
|
||||
init_per_suite(Config) ->
|
||||
emqx_ct_helpers:start_apps([]),
|
||||
Config.
|
||||
|
||||
end_per_suite(_Config) ->
|
||||
emqx_ct_helpers:stop_apps([]).
|
||||
|
||||
t_random_basic(_) ->
|
||||
ok = ensure_config(random),
|
||||
ClientId = <<"ClientId">>,
|
||||
{ok, ConnPid} = emqx_mock_client:start_link(ClientId),
|
||||
{ok, SPid} = emqx_mock_client:open_session(ConnPid, ClientId, internal),
|
||||
Message1 = emqx_message:make(<<"ClientId">>, 2, <<"foo">>, <<"hello">>),
|
||||
emqx_session:subscribe(SPid, [{<<"foo">>, #{qos => 2, share => <<"group1">>}}]),
|
||||
%% wait for the subscription to show up
|
||||
?wait(subscribed(<<"group1">>, <<"foo">>, SPid), 1000),
|
||||
PacketId = 1,
|
||||
emqx_session:publish(SPid, PacketId, Message1),
|
||||
?wait(case emqx_mock_client:get_last_message(ConnPid) of
|
||||
[{publish, 1, _}] -> true;
|
||||
Other -> Other
|
||||
end, 1000),
|
||||
emqx_session:pubrec(SPid, PacketId, reasoncode),
|
||||
emqx_session:pubcomp(SPid, PacketId, reasoncode),
|
||||
emqx_mock_client:close_session(ConnPid),
|
||||
ok.
|
||||
|
||||
%% Start two subscribers share subscribe to "$share/g1/foo/bar"
|
||||
%% Set 'sticky' dispatch strategy, send 1st message to find
|
||||
%% out which member it picked, then close its connection
|
||||
%% send the second message, the message should be 'nack'ed
|
||||
%% by the sticky session and delivered to the 2nd session.
|
||||
%% After the connection for the 2nd session is also closed,
|
||||
%% i.e. when all clients are offline, the following message(s)
|
||||
%% should be delivered randomly.
|
||||
t_no_connection_nack(_) ->
|
||||
ok = ensure_config(sticky),
|
||||
Publisher = <<"publisher">>,
|
||||
Subscriber1 = <<"Subscriber1">>,
|
||||
Subscriber2 = <<"Subscriber2">>,
|
||||
QoS = 1,
|
||||
Group = <<"g1">>,
|
||||
Topic = <<"foo/bar">>,
|
||||
{ok, PubConnPid} = emqx_mock_client:start_link(Publisher),
|
||||
{ok, SubConnPid1} = emqx_mock_client:start_link(Subscriber1),
|
||||
{ok, SubConnPid2} = emqx_mock_client:start_link(Subscriber2),
|
||||
%% allow session to persist after connection shutdown
|
||||
Attrs = #{expiry_interval => timer:seconds(30)},
|
||||
{ok, P_Pid} = emqx_mock_client:open_session(PubConnPid, Publisher, internal, Attrs),
|
||||
{ok, SPid1} = emqx_mock_client:open_session(SubConnPid1, Subscriber1, internal, Attrs),
|
||||
{ok, SPid2} = emqx_mock_client:open_session(SubConnPid2, Subscriber2, internal, Attrs),
|
||||
emqx_session:subscribe(SPid1, [{Topic, #{qos => QoS, share => Group}}]),
|
||||
emqx_session:subscribe(SPid2, [{Topic, #{qos => QoS, share => Group}}]),
|
||||
%% wait for the subscriptions to show up
|
||||
?wait(subscribed(Group, Topic, SPid1), 1000),
|
||||
?wait(subscribed(Group, Topic, SPid2), 1000),
|
||||
MkPayload = fun(PacketId) -> iolist_to_binary(["hello-", integer_to_list(PacketId)]) end,
|
||||
SendF = fun(PacketId) -> emqx_session:publish(P_Pid, PacketId, emqx_message:make(Publisher, QoS, Topic, MkPayload(PacketId))) end,
|
||||
SendF(1),
|
||||
Ref = make_ref(),
|
||||
CasePid = self(),
|
||||
Received =
|
||||
fun(PacketId, ConnPid) ->
|
||||
Payload = MkPayload(PacketId),
|
||||
case emqx_mock_client:get_last_message(ConnPid) of
|
||||
[{publish, _, #message{payload = Payload}}] ->
|
||||
CasePid ! {Ref, PacketId, ConnPid},
|
||||
true;
|
||||
_Other ->
|
||||
false
|
||||
end
|
||||
end,
|
||||
?wait(Received(1, SubConnPid1) orelse Received(1, SubConnPid2), 1000),
|
||||
%% This is the connection which was picked by broker to dispatch (sticky) for 1st message
|
||||
ConnPid = receive {Ref, 1, Pid} -> Pid after 1000 -> error(timeout) end,
|
||||
%% Now kill the connection, expect all following messages to be delivered to the other subscriber.
|
||||
emqx_mock_client:stop(ConnPid),
|
||||
%% sleep then make synced calls to session processes to ensure that
|
||||
%% the connection pid's 'EXIT' message is propagated to the session process
|
||||
%% also to be sure sessions are still alive
|
||||
timer:sleep(2),
|
||||
_ = emqx_session:info(SPid1),
|
||||
_ = emqx_session:info(SPid2),
|
||||
%% Now we know what is the other still alive connection
|
||||
[TheOtherConnPid] = [SubConnPid1, SubConnPid2] -- [ConnPid],
|
||||
%% Send some more messages
|
||||
PacketIdList = lists:seq(2, 10),
|
||||
lists:foreach(fun(Id) ->
|
||||
SendF(Id),
|
||||
?wait(Received(Id, TheOtherConnPid), 1000)
|
||||
end, PacketIdList),
|
||||
%% Now close the 2nd (last connection)
|
||||
emqx_mock_client:stop(TheOtherConnPid),
|
||||
timer:sleep(2),
|
||||
%% both sessions should have conn_pid = undefined
|
||||
?assertEqual({conn_pid, undefined}, lists:keyfind(conn_pid, 1, emqx_session:info(SPid1))),
|
||||
?assertEqual({conn_pid, undefined}, lists:keyfind(conn_pid, 1, emqx_session:info(SPid2))),
|
||||
%% send more messages, but all should be queued in session state
|
||||
lists:foreach(fun(Id) -> SendF(Id) end, PacketIdList),
|
||||
{_, L1} = lists:keyfind(mqueue_len, 1, emqx_session:info(SPid1)),
|
||||
{_, L2} = lists:keyfind(mqueue_len, 1, emqx_session:info(SPid2)),
|
||||
?assertEqual(length(PacketIdList), L1 + L2),
|
||||
%% clean up
|
||||
emqx_mock_client:close_session(PubConnPid),
|
||||
emqx_sm:close_session(SPid1),
|
||||
emqx_sm:close_session(SPid2),
|
||||
ok.
|
||||
|
||||
t_random(_) ->
|
||||
test_two_messages(random).
|
||||
|
||||
t_round_robin(_) ->
|
||||
test_two_messages(round_robin).
|
||||
|
||||
t_sticky(_) ->
|
||||
test_two_messages(sticky).
|
||||
|
||||
t_hash(_) ->
|
||||
test_two_messages(hash, false).
|
||||
|
||||
%% if the original subscriber dies, change to another one alive
|
||||
t_not_so_sticky(_) ->
|
||||
ok = ensure_config(sticky),
|
||||
ClientId1 = <<"ClientId1">>,
|
||||
ClientId2 = <<"ClientId2">>,
|
||||
{ok, ConnPid1} = emqx_mock_client:start_link(ClientId1),
|
||||
{ok, ConnPid2} = emqx_mock_client:start_link(ClientId2),
|
||||
{ok, SPid1} = emqx_mock_client:open_session(ConnPid1, ClientId1, internal),
|
||||
{ok, SPid2} = emqx_mock_client:open_session(ConnPid2, ClientId2, internal),
|
||||
Message1 = emqx_message:make(ClientId1, 0, <<"foo/bar">>, <<"hello1">>),
|
||||
Message2 = emqx_message:make(ClientId1, 0, <<"foo/bar">>, <<"hello2">>),
|
||||
emqx_session:subscribe(SPid1, [{<<"foo/bar">>, #{qos => 0, share => <<"group1">>}}]),
|
||||
%% wait for the subscription to show up
|
||||
?wait(subscribed(<<"group1">>, <<"foo/bar">>, SPid1), 1000),
|
||||
emqx_session:publish(SPid1, 1, Message1),
|
||||
?wait(case emqx_mock_client:get_last_message(ConnPid1) of
|
||||
[{publish, _, #message{payload = <<"hello1">>}}] -> true;
|
||||
Other -> Other
|
||||
end, 1000),
|
||||
emqx_mock_client:close_session(ConnPid1),
|
||||
?wait(not subscribed(<<"group1">>, <<"foo/bar">>, SPid1), 1000),
|
||||
emqx_session:subscribe(SPid2, [{<<"foo/#">>, #{qos => 0, share => <<"group1">>}}]),
|
||||
?wait(subscribed(<<"group1">>, <<"foo/#">>, SPid2), 1000),
|
||||
emqx_session:publish(SPid2, 2, Message2),
|
||||
?wait(case emqx_mock_client:get_last_message(ConnPid2) of
|
||||
[{publish, _, #message{payload = <<"hello2">>}}] -> true;
|
||||
Other -> Other
|
||||
end, 1000),
|
||||
emqx_mock_client:close_session(ConnPid2),
|
||||
?wait(not subscribed(<<"group1">>, <<"foo/#">>, SPid2), 1000),
|
||||
ok.
|
||||
|
||||
test_two_messages(Strategy) ->
|
||||
test_two_messages(Strategy, _WithAck = true).
|
||||
|
||||
test_two_messages(Strategy, WithAck) ->
|
||||
ok = ensure_config(Strategy, WithAck),
|
||||
Topic = <<"foo/bar">>,
|
||||
ClientId1 = <<"ClientId1">>,
|
||||
ClientId2 = <<"ClientId2">>,
|
||||
{ok, ConnPid1} = emqx_mock_client:start_link(ClientId1),
|
||||
{ok, ConnPid2} = emqx_mock_client:start_link(ClientId2),
|
||||
{ok, SPid1} = emqx_mock_client:open_session(ConnPid1, ClientId1, internal),
|
||||
{ok, SPid2} = emqx_mock_client:open_session(ConnPid2, ClientId2, internal),
|
||||
Message1 = emqx_message:make(ClientId1, 0, Topic, <<"hello1">>),
|
||||
Message2 = emqx_message:make(ClientId1, 0, Topic, <<"hello2">>),
|
||||
emqx_session:subscribe(SPid1, [{Topic, #{qos => 0, share => <<"group1">>}}]),
|
||||
emqx_session:subscribe(SPid2, [{Topic, #{qos => 0, share => <<"group1">>}}]),
|
||||
%% wait for the subscription to show up
|
||||
?wait(subscribed(<<"group1">>, Topic, SPid1) andalso
|
||||
subscribed(<<"group1">>, Topic, SPid2), 1000),
|
||||
emqx_broker:publish(Message1),
|
||||
Me = self(),
|
||||
WaitF = fun(ExpectedPayload) ->
|
||||
case last_message(ExpectedPayload, [ConnPid1, ConnPid2]) of
|
||||
{true, Pid} ->
|
||||
Me ! {subscriber, Pid},
|
||||
true;
|
||||
Other ->
|
||||
Other
|
||||
end
|
||||
end,
|
||||
?wait(WaitF(<<"hello1">>), 2000),
|
||||
UsedSubPid1 = receive {subscriber, P1} -> P1 end,
|
||||
emqx_broker:publish(Message2),
|
||||
?wait(WaitF(<<"hello2">>), 2000),
|
||||
UsedSubPid2 = receive {subscriber, P2} -> P2 end,
|
||||
case Strategy of
|
||||
sticky -> ?assert(UsedSubPid1 =:= UsedSubPid2);
|
||||
round_robin -> ?assert(UsedSubPid1 =/= UsedSubPid2);
|
||||
hash -> ?assert(UsedSubPid1 =:= UsedSubPid2);
|
||||
_ -> ok
|
||||
end,
|
||||
emqx_mock_client:close_session(ConnPid1),
|
||||
emqx_mock_client:close_session(ConnPid2),
|
||||
ok.
|
||||
|
||||
last_message(_ExpectedPayload, []) -> <<"not yet?">>;
|
||||
last_message(ExpectedPayload, [Pid | Pids]) ->
|
||||
case emqx_mock_client:get_last_message(Pid) of
|
||||
[{publish, _, #message{payload = ExpectedPayload}}] -> {true, Pid};
|
||||
_Other -> last_message(ExpectedPayload, Pids)
|
||||
end.
|
||||
|
||||
%%------------------------------------------------------------------------------
|
||||
%% help functions
|
||||
%%------------------------------------------------------------------------------
|
||||
|
||||
ensure_config(Strategy) ->
|
||||
ensure_config(Strategy, _AckEnabled = true).
|
||||
|
||||
ensure_config(Strategy, AckEnabled) ->
|
||||
application:set_env(emqx, shared_subscription_strategy, Strategy),
|
||||
application:set_env(emqx, shared_dispatch_ack_enabled, AckEnabled),
|
||||
ok.
|
||||
|
||||
subscribed(Group, Topic, Pid) ->
|
||||
lists:member(Pid, emqx_shared_sub:subscribers(Group, Topic)).
|
|
@ -1,115 +0,0 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2019 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_sm_SUITE).
|
||||
|
||||
-include("emqx.hrl").
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
-include_lib("common_test/include/ct.hrl").
|
||||
|
||||
-compile(export_all).
|
||||
-compile(nowarn_export_all).
|
||||
|
||||
-define(ATTRS, #{clean_start => true,
|
||||
client_id => <<"client">>,
|
||||
zone => internal,
|
||||
username => <<"emqx">>,
|
||||
expiry_interval => 0,
|
||||
max_inflight => 0,
|
||||
topic_alias_maximum => 0,
|
||||
will_msg => undefined}).
|
||||
|
||||
all() -> [{group, registry}, {group, ets}].
|
||||
|
||||
groups() ->
|
||||
Cases =
|
||||
[ t_resume_session,
|
||||
t_discard_session,
|
||||
t_register_unregister_session,
|
||||
t_get_set_session_attrs,
|
||||
t_get_set_session_stats,
|
||||
t_lookup_session_pids],
|
||||
[ {registry, [non_parallel_tests], Cases},
|
||||
{ets, [non_parallel_tests], Cases}].
|
||||
|
||||
init_per_suite(Config) ->
|
||||
Config.
|
||||
|
||||
end_per_suite(_Config) ->
|
||||
ok.
|
||||
|
||||
init_per_group(registry, Config) ->
|
||||
emqx_ct_helpers:start_apps([], fun enable_session_registry/1),
|
||||
Config;
|
||||
init_per_group(ets, Config) ->
|
||||
emqx_ct_helpers:start_apps([], fun disable_session_registry/1),
|
||||
Config.
|
||||
|
||||
end_per_group(_, _Config) ->
|
||||
emqx_ct_helpers:stop_apps([]).
|
||||
|
||||
init_per_testcase(_All, Config) ->
|
||||
{ok, SPid} = emqx_sm:open_session(?ATTRS#{conn_pid => self()}),
|
||||
[{session_pid, SPid}|Config].
|
||||
|
||||
end_per_testcase(_All, Config) ->
|
||||
emqx_sm:close_session(?config(session_pid, Config)),
|
||||
receive
|
||||
{shutdown, normal} -> ok
|
||||
after 500 -> ct:fail({timeout, wait_session_shutdown})
|
||||
end.
|
||||
|
||||
enable_session_registry(_) ->
|
||||
application:set_env(emqx, enable_session_registry, true),
|
||||
ok.
|
||||
|
||||
disable_session_registry(_) ->
|
||||
application:set_env(emqx, enable_session_registry, false),
|
||||
ok.
|
||||
|
||||
t_resume_session(Config) ->
|
||||
?assertEqual({ok, ?config(session_pid, Config)}, emqx_sm:resume_session(<<"client">>, ?ATTRS#{conn_pid => self()})).
|
||||
|
||||
t_discard_session(_) ->
|
||||
?assertEqual(ok, emqx_sm:discard_session(<<"client1">>)).
|
||||
|
||||
t_register_unregister_session(_) ->
|
||||
Pid = self(),
|
||||
?assertEqual(ok, emqx_sm:register_session(<<"client">>)),
|
||||
?assertEqual(ok, emqx_sm:register_session(<<"client">>, Pid)),
|
||||
?assertEqual(ok, emqx_sm:unregister_session(<<"client">>)),
|
||||
?assertEqual(ok, emqx_sm:unregister_session(<<"client">>), Pid).
|
||||
|
||||
t_get_set_session_attrs(Config) ->
|
||||
SPid = ?config(session_pid, Config),
|
||||
ClientPid0 = spawn(fun() -> receive _ -> ok end end),
|
||||
?assertEqual(true, emqx_sm:set_session_attrs(<<"client">>, [?ATTRS#{conn_pid => ClientPid0}])),
|
||||
?assertEqual(true, emqx_sm:set_session_attrs(<<"client">>, SPid, [?ATTRS#{conn_pid => ClientPid0}])),
|
||||
[SAttr0] = emqx_sm:get_session_attrs(<<"client">>, SPid),
|
||||
?assertEqual(ClientPid0, maps:get(conn_pid, SAttr0)),
|
||||
?assertEqual(true, emqx_sm:set_session_attrs(<<"client">>, SPid, [?ATTRS#{conn_pid => self()}])),
|
||||
[SAttr1] = emqx_sm:get_session_attrs(<<"client">>, SPid),
|
||||
?assertEqual(self(), maps:get(conn_pid, SAttr1)).
|
||||
|
||||
t_get_set_session_stats(Config) ->
|
||||
SPid = ?config(session_pid, Config),
|
||||
?assertEqual(true, emqx_sm:set_session_stats(<<"client">>, [{inflight, 10}])),
|
||||
?assertEqual(true, emqx_sm:set_session_stats(<<"client">>, SPid, [{inflight, 10}])),
|
||||
?assertEqual([{inflight, 10}], emqx_sm:get_session_stats(<<"client">>, SPid)).
|
||||
|
||||
t_lookup_session_pids(Config) ->
|
||||
SPid = ?config(session_pid, Config),
|
||||
?assertEqual([SPid], emqx_sm:lookup_session_pids(<<"client">>)).
|
|
@ -14,36 +14,41 @@
|
|||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
-module(emqx_stats_tests).
|
||||
-module(emqx_stats_SUITE).
|
||||
|
||||
-compile(export_all).
|
||||
-compile(nowarn_export_all).
|
||||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
get_state_test() ->
|
||||
all() -> emqx_ct:all(?MODULE).
|
||||
|
||||
t_get_state() ->
|
||||
with_proc(fun() ->
|
||||
SetConnsCount = emqx_stats:statsfun('connections.count'),
|
||||
SetConnsCount(1),
|
||||
1 = emqx_stats:getstat('connections.count'),
|
||||
?assertEqual(1, emqx_stats:getstat('connections.count')),
|
||||
emqx_stats:setstat('connections.count', 2),
|
||||
2 = emqx_stats:getstat('connections.count'),
|
||||
?assertEqual(2, emqx_stats:getstat('connections.count')),
|
||||
emqx_stats:setstat('connections.count', 'connections.max', 3),
|
||||
timer:sleep(100),
|
||||
3 = emqx_stats:getstat('connections.count'),
|
||||
3 = emqx_stats:getstat('connections.max'),
|
||||
?assertEqual(3, emqx_stats:getstat('connections.count')),
|
||||
?assertEqual(3, emqx_stats:getstat('connections.max')),
|
||||
emqx_stats:setstat('connections.count', 'connections.max', 2),
|
||||
timer:sleep(100),
|
||||
2 = emqx_stats:getstat('connections.count'),
|
||||
3 = emqx_stats:getstat('connections.max'),
|
||||
?assertEqual(2, emqx_stats:getstat('connections.count')),
|
||||
?assertEqual(3, emqx_stats:getstat('connections.max')),
|
||||
SetConns = emqx_stats:statsfun('connections.count', 'connections.max'),
|
||||
SetConns(4),
|
||||
timer:sleep(100),
|
||||
4 = emqx_stats:getstat('connections.count'),
|
||||
4 = emqx_stats:getstat('connections.max'),
|
||||
?assertEqual(4, emqx_stats:getstat('connections.count')),
|
||||
?assertEqual(4, emqx_stats:getstat('connections.max')),
|
||||
Conns = emqx_stats:getstats(),
|
||||
4 = proplists:get_value('connections.count', Conns),
|
||||
4 = proplists:get_value('connections.max', Conns)
|
||||
?assertEqual(4, proplists:get_value('connections.count', Conns)),
|
||||
?assertEqual(4, proplists:get_value('connections.max', Conns))
|
||||
end).
|
||||
|
||||
update_interval_test() ->
|
||||
t_update_interval() ->
|
||||
TickMs = 200,
|
||||
with_proc(fun() ->
|
||||
SleepMs = TickMs * 2 + TickMs div 2, %% sleep for 2.5 ticks
|
|
@ -19,21 +19,27 @@
|
|||
-compile(export_all).
|
||||
-compile(nowarn_export_all).
|
||||
|
||||
-include("emqx_mqtt.hrl").
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
-include_lib("common_test/include/ct.hrl").
|
||||
-define(SYSMON, emqx_sys_mon).
|
||||
|
||||
-include("emqx_mqtt.hrl").
|
||||
|
||||
-define(SYSMONPID, emqx_sys_mon).
|
||||
-define(INPUTINFO, [{self(), long_gc, concat_str("long_gc warning: pid = ~p, info: ~p", self(), "hello"), "hello"},
|
||||
{self(), long_schedule, concat_str("long_schedule warning: pid = ~p, info: ~p", self(), "hello"), "hello"},
|
||||
{self(), busy_port, concat_str("busy_port warning: suspid = ~p, port = ~p", self(), list_to_port("#Port<0.4>")), list_to_port("#Port<0.4>")},
|
||||
{self(), busy_dist_port, concat_str("busy_dist_port warning: suspid = ~p, port = ~p", self(), list_to_port("#Port<0.4>")),list_to_port("#Port<0.4>")},
|
||||
{list_to_port("#Port<0.4>"), long_schedule, concat_str("long_schedule warning: port = ~p, info: ~p", list_to_port("#Port<0.4>"), "hello"), "hello"}
|
||||
-define(INPUTINFO, [{self(), long_gc,
|
||||
concat_str("long_gc warning: pid = ~p, info: ~p", self(), "hello"), "hello"},
|
||||
{self(), long_schedule,
|
||||
concat_str("long_schedule warning: pid = ~p, info: ~p", self(), "hello"), "hello"},
|
||||
{self(), busy_port,
|
||||
concat_str("busy_port warning: suspid = ~p, port = ~p",
|
||||
self(), list_to_port("#Port<0.4>")), list_to_port("#Port<0.4>")},
|
||||
{self(), busy_dist_port,
|
||||
concat_str("busy_dist_port warning: suspid = ~p, port = ~p",
|
||||
self(), list_to_port("#Port<0.4>")),list_to_port("#Port<0.4>")},
|
||||
{list_to_port("#Port<0.4>"), long_schedule,
|
||||
concat_str("long_schedule warning: port = ~p, info: ~p",
|
||||
list_to_port("#Port<0.4>"), "hello"), "hello"}
|
||||
]).
|
||||
|
||||
all() -> [t_sys_mon].
|
||||
all() -> emqx_ct:all(?MODULE).
|
||||
|
||||
init_per_suite(Config) ->
|
||||
emqx_ct_helpers:start_apps([]),
|
||||
|
@ -43,16 +49,17 @@ end_per_suite(_Config) ->
|
|||
emqx_ct_helpers:stop_apps([]).
|
||||
|
||||
t_sys_mon(_Config) ->
|
||||
lists:foreach(fun({PidOrPort, SysMonName,ValidateInfo, InfoOrPort}) ->
|
||||
validate_sys_mon_info(PidOrPort, SysMonName,ValidateInfo, InfoOrPort)
|
||||
end, ?INPUTINFO).
|
||||
lists:foreach(
|
||||
fun({PidOrPort, SysMonName,ValidateInfo, InfoOrPort}) ->
|
||||
validate_sys_mon_info(PidOrPort, SysMonName,ValidateInfo, InfoOrPort)
|
||||
end, ?INPUTINFO).
|
||||
|
||||
validate_sys_mon_info(PidOrPort, SysMonName,ValidateInfo, InfoOrPort) ->
|
||||
{ok, C} = emqx_client:start_link([{host, "localhost"}]),
|
||||
{ok, _} = emqx_client:connect(C),
|
||||
emqx_client:subscribe(C, emqx_topic:systop(lists:concat(['sysmon/', SysMonName])), qos1),
|
||||
timer:sleep(100),
|
||||
?SYSMONPID ! {monitor, PidOrPort, SysMonName, InfoOrPort},
|
||||
?SYSMON ! {monitor, PidOrPort, SysMonName, InfoOrPort},
|
||||
receive
|
||||
{publish, #{payload := Info}} ->
|
||||
?assertEqual(ValidateInfo, binary_to_list(Info)),
|
||||
|
|
|
@ -19,12 +19,27 @@
|
|||
-compile(export_all).
|
||||
-compile(nowarn_export_all).
|
||||
|
||||
all() -> [t_new].
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
-define(TAB, ?MODULE).
|
||||
|
||||
all() -> emqx_ct:all(?MODULE).
|
||||
|
||||
t_new(_) ->
|
||||
ok = emqx_tables:new(test_table, [{read_concurrency, true}]),
|
||||
ets:insert(test_table, {key, 100}),
|
||||
ok = emqx_tables:new(test_table, [{read_concurrency, true}]),
|
||||
100 = ets:lookup_element(test_table, key, 2),
|
||||
ok = emqx_tables:delete(test_table),
|
||||
ok = emqx_tables:delete(test_table).
|
||||
ok = emqx_tables:new(?TAB),
|
||||
ok = emqx_tables:new(?TAB, [{read_concurrency, true}]),
|
||||
?assertEqual(?TAB, ets:info(?TAB, name)).
|
||||
|
||||
t_lookup_value(_) ->
|
||||
ok = emqx_tables:new(?TAB, []),
|
||||
true = ets:insert(?TAB, {key, val}),
|
||||
?assertEqual(val, emqx_tables:lookup_value(?TAB, key)),
|
||||
?assertEqual(undefined, emqx_tables:lookup_value(?TAB, badkey)).
|
||||
|
||||
t_delete(_) ->
|
||||
ok = emqx_tables:new(?TAB, []),
|
||||
?assertEqual(?TAB, ets:info(?TAB, name)),
|
||||
ok = emqx_tables:delete(?TAB),
|
||||
ok = emqx_tables:delete(?TAB),
|
||||
?assertEqual(undefined, ets:info(?TAB, name)).
|
||||
|
||||
|
|
|
@ -16,14 +16,19 @@
|
|||
|
||||
-module(emqx_time_SUITE).
|
||||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
-compile(export_all).
|
||||
-compile(nowarn_export_all).
|
||||
|
||||
all() -> [t_time_now_to].
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
all() -> emqx_ct:all(?MODULE).
|
||||
|
||||
t_seed(_) ->
|
||||
?assert(is_tuple(emqx_time:seed())).
|
||||
|
||||
t_now_secs(_) ->
|
||||
?assert(emqx_time:now_secs() =< emqx_time:now_secs(os:timestamp())).
|
||||
|
||||
t_now_ms(_) ->
|
||||
?assert(emqx_time:now_ms() =< emqx_time:now_ms(os:timestamp())).
|
||||
|
||||
t_time_now_to(_) ->
|
||||
emqx_time:seed(),
|
||||
emqx_time:now_secs(),
|
||||
emqx_time:now_ms().
|
||||
|
|
|
@ -16,44 +16,29 @@
|
|||
|
||||
-module(emqx_topic_SUITE).
|
||||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
%% CT
|
||||
-compile(export_all).
|
||||
-compile(nowarn_export_all).
|
||||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
-include_lib("emqx_ct_helpers/include/emqx_ct.hrl").
|
||||
|
||||
-import(emqx_topic,
|
||||
[ wildcard/1
|
||||
, match/2
|
||||
, validate/1
|
||||
, triples/1
|
||||
, prepend/2
|
||||
, join/1
|
||||
, words/1
|
||||
, systop/1
|
||||
, feed_var/3
|
||||
, parse/1
|
||||
, parse/2
|
||||
]).
|
||||
|
||||
-define(N, 10000).
|
||||
-define(N, 100000).
|
||||
|
||||
all() ->
|
||||
[t_wildcard,
|
||||
t_match, t_match2, t_match3,
|
||||
t_validate,
|
||||
t_triples,
|
||||
t_join,
|
||||
t_levels,
|
||||
t_tokens,
|
||||
t_words,
|
||||
t_systop,
|
||||
t_feed_var,
|
||||
t_sys_match,
|
||||
't_#_match',
|
||||
t_sigle_level_validate,
|
||||
t_sigle_level_match,
|
||||
t_match_perf,
|
||||
t_triples_perf,
|
||||
t_parse].
|
||||
all() -> emqx_ct:all(?MODULE).
|
||||
|
||||
t_wildcard(_) ->
|
||||
true = wildcard(<<"a/b/#">>),
|
||||
|
@ -61,7 +46,7 @@ t_wildcard(_) ->
|
|||
false = wildcard(<<"">>),
|
||||
false = wildcard(<<"a/b/c">>).
|
||||
|
||||
t_match(_) ->
|
||||
t_match1(_) ->
|
||||
true = match(<<"a/b/c">>, <<"a/b/+">>),
|
||||
true = match(<<"a/b/c">>, <<"a/#">>),
|
||||
true = match(<<"abcd/ef/g">>, <<"#">>),
|
||||
|
@ -132,74 +117,74 @@ t_match_perf(_) ->
|
|||
Name = <<"/abkc/19383/192939/akakdkkdkak/xxxyyuya/akakak">>,
|
||||
Filter = <<"/abkc/19383/+/akakdkkdkak/#">>,
|
||||
true = match(Name, Filter),
|
||||
{Time, _} = timer:tc(fun() ->
|
||||
[match(Name, Filter) || _I <- lists:seq(1, ?N)]
|
||||
end),
|
||||
io:format("Time for match: ~p(micro)", [Time/?N]).
|
||||
ok = bench('match/2', fun emqx_topic:match/2, [Name, Filter]).
|
||||
|
||||
t_validate(_) ->
|
||||
true = validate({name, <<"abc/de/f">>}),
|
||||
true = validate({filter, <<"abc/+/f">>}),
|
||||
true = validate({filter, <<"abc/#">>}),
|
||||
true = validate({filter, <<"x">>}),
|
||||
true = validate({name, <<"x//y">>}),
|
||||
true = validate({filter, <<"sport/tennis/#">>}),
|
||||
catch validate({name, <<>>}),
|
||||
catch validate({name, long_topic()}),
|
||||
catch validate({name, <<"abc/#">>}),
|
||||
catch validate({filter, <<"abc/#/1">>}),
|
||||
catch validate({filter, <<"abc/#xzy/+">>}),
|
||||
catch validate({filter, <<"abc/xzy/+9827">>}),
|
||||
catch validate({filter, <<"sport/tennis#">>}),
|
||||
catch validate({filter, <<"sport/tennis/#/ranking">>}),
|
||||
ok.
|
||||
true = validate(<<"a/+/#">>),
|
||||
true = validate(<<"a/b/c/d">>),
|
||||
true = validate({name, <<"abc/de/f">>}),
|
||||
true = validate({filter, <<"abc/+/f">>}),
|
||||
true = validate({filter, <<"abc/#">>}),
|
||||
true = validate({filter, <<"x">>}),
|
||||
true = validate({name, <<"x//y">>}),
|
||||
true = validate({filter, <<"sport/tennis/#">>}),
|
||||
ok = ?catch_error(empty_topic, validate({name, <<>>})),
|
||||
ok = ?catch_error(topic_name_error, validate({name, <<"abc/#">>})),
|
||||
ok = ?catch_error(topic_too_long, validate({name, long_topic()})),
|
||||
ok = ?catch_error('topic_invalid_#', validate({filter, <<"abc/#/1">>})),
|
||||
ok = ?catch_error(topic_invalid_char, validate({filter, <<"abc/#xzy/+">>})),
|
||||
ok = ?catch_error(topic_invalid_char, validate({filter, <<"abc/xzy/+9827">>})),
|
||||
ok = ?catch_error(topic_invalid_char, validate({filter, <<"sport/tennis#">>})),
|
||||
ok = ?catch_error('topic_invalid_#', validate({filter, <<"sport/tennis/#/ranking">>})).
|
||||
|
||||
t_sigle_level_validate(_) ->
|
||||
true = validate({filter, <<"+">>}),
|
||||
true = validate({filter, <<"+/tennis/#">>}),
|
||||
true = validate({filter, <<"sport/+/player1">>}),
|
||||
catch validate({filter, <<"sport+">>}),
|
||||
ok.
|
||||
true = validate({filter, <<"+">>}),
|
||||
true = validate({filter, <<"+/tennis/#">>}),
|
||||
true = validate({filter, <<"sport/+/player1">>}),
|
||||
ok = ?catch_error(topic_invalid_char, validate({filter, <<"sport+">>})).
|
||||
|
||||
t_triples(_) ->
|
||||
Triples = [{root,<<"a">>,<<"a">>},
|
||||
{<<"a">>,<<"b">>,<<"a/b">>},
|
||||
{<<"a/b">>,<<"c">>,<<"a/b/c">>}],
|
||||
Triples = triples(<<"a/b/c">>).
|
||||
?assertEqual(Triples, triples(<<"a/b/c">>)).
|
||||
|
||||
t_triples_perf(_) ->
|
||||
Topic = <<"/abkc/19383/192939/akakdkkdkak/xxxyyuya/akakak">>,
|
||||
{Time, _} = timer:tc(fun() ->
|
||||
[triples(Topic) || _I <- lists:seq(1, ?N)]
|
||||
end),
|
||||
io:format("Time for triples: ~p(micro)", [Time/?N]).
|
||||
ok = bench('triples/1', fun emqx_topic:triples/1, [Topic]).
|
||||
|
||||
t_prepend(_) ->
|
||||
?assertEqual(<<"a/b/c">>, prepend(root, <<"a/b/c">>)),
|
||||
?assertEqual(<<"ab">>, prepend(undefined, <<"ab">>)),
|
||||
?assertEqual(<<"a/b">>, prepend(<<>>, <<"a/b">>)),
|
||||
?assertEqual(<<"x/a/b">>, prepend("x/", <<"a/b">>)),
|
||||
?assertEqual(<<"x/y/a/b">>, prepend(<<"x/y">>, <<"a/b">>)),
|
||||
?assertEqual(<<"+/a/b">>, prepend('+', <<"a/b">>)).
|
||||
|
||||
t_levels(_) ->
|
||||
?assertEqual(3, emqx_topic:levels(<<"a/+/#">>)),
|
||||
?assertEqual(4, emqx_topic:levels(<<"a/b/c/d">>)).
|
||||
|
||||
t_tokens(_) ->
|
||||
?assertEqual([<<"a">>, <<"b">>, <<"+">>, <<"#">>], emqx_topic:tokens(<<"a/b/+/#">>)).
|
||||
?assertEqual([<<"a">>, <<"b">>, <<"+">>, <<"#">>],
|
||||
emqx_topic:tokens(<<"a/b/+/#">>)).
|
||||
|
||||
t_words(_) ->
|
||||
['', <<"a">>, '+', '#'] = words(<<"/a/+/#">>),
|
||||
['', <<"abkc">>, <<"19383">>, '+', <<"akakdkkdkak">>, '#'] = words(<<"/abkc/19383/+/akakdkkdkak/#">>),
|
||||
{Time, _} = timer:tc(fun() ->
|
||||
[words(<<"/abkc/19383/+/akakdkkdkak/#">>) || _I <- lists:seq(1, ?N)]
|
||||
end),
|
||||
io:format("Time for words: ~p(micro)", [Time/?N]),
|
||||
{Time2, _} = timer:tc(fun() ->
|
||||
[binary:split(<<"/abkc/19383/+/akakdkkdkak/#">>, <<"/">>, [global]) || _I <- lists:seq(1, ?N)]
|
||||
end),
|
||||
io:format("Time for binary:split: ~p(micro)", [Time2/?N]).
|
||||
Topic = <<"/abkc/19383/+/akakdkkdkak/#">>,
|
||||
?assertEqual(['', <<"a">>, '+', '#'], words(<<"/a/+/#">>)),
|
||||
?assertEqual(['', <<"abkc">>, <<"19383">>, '+', <<"akakdkkdkak">>, '#'], words(Topic)),
|
||||
ok = bench('words/1', fun emqx_topic:words/1, [Topic]),
|
||||
BSplit = fun(Bin) -> binary:split(Bin, <<"/">>, [global]) end,
|
||||
ok = bench('binary:split/3', BSplit, [Topic]).
|
||||
|
||||
t_join(_) ->
|
||||
<<>> = join([]),
|
||||
<<"x">> = join([<<"x">>]),
|
||||
<<"#">> = join(['#']),
|
||||
<<"+//#">> = join(['+', '', '#']),
|
||||
<<"x/y/z/+">> = join([<<"x">>, <<"y">>, <<"z">>, '+']),
|
||||
<<"/ab/cd/ef/">> = join(words(<<"/ab/cd/ef/">>)),
|
||||
<<"ab/+/#">> = join(words(<<"ab/+/#">>)).
|
||||
?assertEqual(<<>>, join([])),
|
||||
?assertEqual(<<"x">>, join([<<"x">>])),
|
||||
?assertEqual(<<"#">>, join(['#'])),
|
||||
?assertEqual(<<"+//#">>, join(['+', '', '#'])),
|
||||
?assertEqual(<<"x/y/z/+">>, join([<<"x">>, <<"y">>, <<"z">>, '+'])),
|
||||
?assertEqual(<<"/ab/cd/ef/">>, join(words(<<"/ab/cd/ef/">>))),
|
||||
?assertEqual(<<"ab/+/#">>, join(words(<<"ab/+/#">>))).
|
||||
|
||||
t_systop(_) ->
|
||||
SysTop1 = iolist_to_binary(["$SYS/brokers/", atom_to_list(node()), "/xyz"]),
|
||||
|
@ -219,12 +204,29 @@ long_topic() ->
|
|||
iolist_to_binary([[integer_to_list(I), "/"] || I <- lists:seq(0, 10000)]).
|
||||
|
||||
t_parse(_) ->
|
||||
ok = ?catch_error({invalid_topic_filter, <<"$queue/t">>},
|
||||
parse(<<"$queue/t">>, #{share => <<"g">>})),
|
||||
ok = ?catch_error({invalid_topic_filter, <<"$share/g/t">>},
|
||||
parse(<<"$share/g/t">>, #{share => <<"g">>})),
|
||||
ok = ?catch_error({invalid_topic_filter, <<"$share/t">>},
|
||||
parse(<<"$share/t">>)),
|
||||
ok = ?catch_error({invalid_topic_filter, <<"$share/+/t">>},
|
||||
parse(<<"$share/+/t">>)),
|
||||
?assertEqual({<<"a/b/+/#">>, #{}}, parse(<<"a/b/+/#">>)),
|
||||
?assertEqual({<<"a/b/+/#">>, #{qos => 1}}, parse({<<"a/b/+/#">>, #{qos => 1}})),
|
||||
?assertEqual({<<"topic">>, #{ share => <<"$queue">> }}, parse(<<"$queue/topic">>)),
|
||||
?assertEqual({<<"topic">>, #{ share => <<"group">>}}, parse(<<"$share/group/topic">>)),
|
||||
?assertEqual({<<"topic">>, #{share => <<"$queue">>}}, parse(<<"$queue/topic">>)),
|
||||
?assertEqual({<<"topic">>, #{share => <<"group">>}}, parse(<<"$share/group/topic">>)),
|
||||
%% The '$local' and '$fastlane' topics have been deprecated.
|
||||
?assertEqual({<<"$local/topic">>, #{}}, parse(<<"$local/topic">>)),
|
||||
?assertEqual({<<"$local/$queue/topic">>, #{}}, parse(<<"$local/$queue/topic">>)),
|
||||
?assertEqual({<<"$local/$share/group/a/b/c">>, #{}}, parse(<<"$local/$share/group/a/b/c">>)),
|
||||
?assertEqual({<<"$fastlane/topic">>, #{}}, parse(<<"$fastlane/topic">>)).
|
||||
|
||||
bench(Case, Fun, Args) ->
|
||||
{Time, ok} = timer:tc(fun lists:foreach/2,
|
||||
[fun(_) -> apply(Fun, Args) end,
|
||||
lists:seq(1, ?N)
|
||||
]),
|
||||
ct:pal("Time consumed by ~s: ~.3f(us)~nCall ~s per second: ~w",
|
||||
[Case, Time/?N, Case, (?N * 1000000) div Time]).
|
||||
|
||||
|
|
|
@ -20,10 +20,9 @@
|
|||
-compile(nowarn_export_all).
|
||||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
-include_lib("common_test/include/ct.hrl").
|
||||
|
||||
all() -> [start_traces].
|
||||
all() -> [t_start_traces].
|
||||
|
||||
init_per_suite(Config) ->
|
||||
emqx_ct_helpers:start_apps([]),
|
||||
|
@ -32,7 +31,7 @@ init_per_suite(Config) ->
|
|||
end_per_suite(_Config) ->
|
||||
emqx_ct_helpers:stop_apps([]).
|
||||
|
||||
start_traces(_Config) ->
|
||||
t_start_traces(_Config) ->
|
||||
{ok, T} = emqx_client:start_link([{host, "localhost"},
|
||||
{client_id, <<"client">>},
|
||||
{username, <<"testuser">>},
|
||||
|
|
|
@ -25,8 +25,7 @@
|
|||
-define(TRIE, emqx_trie).
|
||||
-define(TRIE_TABS, [emqx_trie, emqx_trie_node]).
|
||||
|
||||
all() ->
|
||||
[t_mnesia, t_insert, t_match, t_match2, t_match3, t_empty, t_delete, t_delete2, t_delete3].
|
||||
all() -> emqx_ct:all(?MODULE).
|
||||
|
||||
init_per_suite(Config) ->
|
||||
application:load(emqx),
|
||||
|
@ -51,7 +50,8 @@ t_insert(_) ->
|
|||
TN = #trie_node{node_id = <<"sensor">>,
|
||||
edge_count = 3,
|
||||
topic = <<"sensor">>,
|
||||
flags = undefined},
|
||||
flags = undefined
|
||||
},
|
||||
Fun = fun() ->
|
||||
?TRIE:insert(<<"sensor/1/metric/2">>),
|
||||
?TRIE:insert(<<"sensor/+/#">>),
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
-compile(export_all).
|
||||
-compile(nowarn_export_all).
|
||||
|
||||
-include_lib("common_test/include/ct.hrl").
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
-define(SYSTEM_INFO, [allocated_areas,
|
||||
allocator,
|
||||
|
@ -94,82 +94,79 @@
|
|||
min_heap_size]).
|
||||
%fullsweep_after]).
|
||||
|
||||
all() -> emqx_ct:all(?MODULE).
|
||||
|
||||
t_load(_Config) ->
|
||||
?assertMatch([{load1, _},
|
||||
{load5, _},
|
||||
{load15, _}
|
||||
], emqx_vm:loads()).
|
||||
|
||||
all() ->
|
||||
[load, systeminfo, mem_info, process_list, process_info, process_gc,
|
||||
get_ets_list, get_ets_info, get_ets_object, get_port_types, get_port_info,
|
||||
scheduler_usage, get_memory, microsecs, schedulers, get_process_group_leader_info,
|
||||
get_process_limit].
|
||||
|
||||
load(_Config) ->
|
||||
Loads = emqx_vm:loads(),
|
||||
[{load1, _}, {load5, _}, {load15, _}] = Loads.
|
||||
|
||||
systeminfo(_Config) ->
|
||||
t_systeminfo(_Config) ->
|
||||
Keys = [Key || {Key, _} <- emqx_vm:get_system_info()],
|
||||
?SYSTEM_INFO = Keys.
|
||||
|
||||
mem_info(_Config) ->
|
||||
t_mem_info(_Config) ->
|
||||
application:ensure_all_started(os_mon),
|
||||
MemInfo = emqx_vm:mem_info(),
|
||||
[{total_memory, _},
|
||||
{used_memory, _}]= MemInfo,
|
||||
application:stop(os_mon).
|
||||
|
||||
process_list(_Config) ->
|
||||
t_process_list(_Config) ->
|
||||
Pid = self(),
|
||||
ProcessInfo = emqx_vm:get_process_list(),
|
||||
true = lists:member({pid, Pid}, lists:concat(ProcessInfo)).
|
||||
|
||||
process_info(_Config) ->
|
||||
t_process_info(_Config) ->
|
||||
ProcessInfos = emqx_vm:get_process_info(),
|
||||
ProcessInfo = lists:last(ProcessInfos),
|
||||
Keys = [K || {K, _V}<- ProcessInfo],
|
||||
?PROCESS_INFO = Keys.
|
||||
|
||||
process_gc(_Config) ->
|
||||
t_process_gc(_Config) ->
|
||||
ProcessGcs = emqx_vm:get_process_gc(),
|
||||
ProcessGc = lists:last(ProcessGcs),
|
||||
Keys = [K || {K, _V}<- ProcessGc],
|
||||
?PROCESS_GC = Keys.
|
||||
|
||||
get_ets_list(_Config) ->
|
||||
|
||||
t_get_ets_list(_Config) ->
|
||||
ets:new(test, [named_table]),
|
||||
Ets = emqx_vm:get_ets_list(),
|
||||
true = lists:member(test, Ets).
|
||||
|
||||
get_ets_info(_Config) ->
|
||||
t_get_ets_info(_Config) ->
|
||||
ets:new(test, [named_table]),
|
||||
[] = emqx_vm:get_ets_info(test1),
|
||||
EtsInfo = emqx_vm:get_ets_info(test),
|
||||
test = proplists:get_value(name, EtsInfo).
|
||||
|
||||
get_ets_object(_Config) ->
|
||||
t_get_ets_object(_Config) ->
|
||||
ets:new(test, [named_table]),
|
||||
ets:insert(test, {k, v}),
|
||||
[{k, v}] = emqx_vm:get_ets_object(test).
|
||||
|
||||
get_port_types(_Config) ->
|
||||
t_get_port_types(_Config) ->
|
||||
emqx_vm:get_port_types().
|
||||
|
||||
get_port_info(_Config) ->
|
||||
t_get_port_info(_Config) ->
|
||||
emqx_vm:get_port_info().
|
||||
|
||||
scheduler_usage(_Config) ->
|
||||
t_scheduler_usage(_Config) ->
|
||||
emqx_vm:scheduler_usage(5000).
|
||||
|
||||
get_memory(_Config) ->
|
||||
t_get_memory(_Config) ->
|
||||
emqx_vm:get_memory().
|
||||
|
||||
microsecs(_Config) ->
|
||||
|
||||
t_microsecs(_Config) ->
|
||||
emqx_vm:microsecs().
|
||||
|
||||
schedulers(_Config) ->
|
||||
t_schedulers(_Config) ->
|
||||
emqx_vm:schedulers().
|
||||
|
||||
get_process_group_leader_info(_Config) ->
|
||||
t_get_process_group_leader_info(_Config) ->
|
||||
emqx_vm:get_process_group_leader_info(self()).
|
||||
|
||||
get_process_limit(_Config) ->
|
||||
t_get_process_limit(_Config) ->
|
||||
emqx_vm:get_process_limit().
|
||||
|
||||
|
|
|
@ -21,8 +21,6 @@
|
|||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
-include_lib("common_test/include/ct.hrl").
|
||||
|
||||
-define(WAIT(PATTERN, TIMEOUT),
|
||||
receive
|
||||
PATTERN ->
|
||||
|
@ -32,7 +30,7 @@
|
|||
error(timeout)
|
||||
end).
|
||||
|
||||
all() -> [t_api].
|
||||
all() -> emqx_ct:all(?MODULE).
|
||||
|
||||
init_per_suite(Config) ->
|
||||
application:ensure_all_started(sasl),
|
||||
|
@ -75,3 +73,4 @@ t_api(_) ->
|
|||
after
|
||||
meck:unload(alarm_handler)
|
||||
end.
|
||||
|
||||
|
|
|
@ -1,144 +0,0 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2019 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_ws_channel_SUITE).
|
||||
|
||||
-compile(export_all).
|
||||
-compile(nowarn_export_all).
|
||||
|
||||
-include("emqx_mqtt.hrl").
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
-include_lib("common_test/include/ct.hrl").
|
||||
|
||||
-define(CLIENT, ?CONNECT_PACKET(#mqtt_packet_connect{
|
||||
client_id = <<"mqtt_client">>,
|
||||
username = <<"admin">>,
|
||||
password = <<"public">>})).
|
||||
|
||||
-define(WILL_TOPIC, <<"test/websocket/will">>).
|
||||
|
||||
-define(WILL_CLIENT, ?CONNECT_PACKET(#mqtt_packet_connect{
|
||||
client_id = <<"mqtt_client">>,
|
||||
username = <<"admin">>,
|
||||
password = <<"public">>,
|
||||
will_flag = true,
|
||||
will_qos = ?QOS_1,
|
||||
will_topic = ?WILL_TOPIC,
|
||||
will_payload = <<"payload">>
|
||||
})).
|
||||
|
||||
all() ->
|
||||
[ t_ws_connect_api
|
||||
, t_ws_auth_failure
|
||||
, t_ws_other_type_frame
|
||||
, t_ws_will
|
||||
].
|
||||
|
||||
init_per_suite(Config) ->
|
||||
emqx_ct_helpers:start_apps([]),
|
||||
Config.
|
||||
|
||||
end_per_suite(_Config) ->
|
||||
emqx_ct_helpers:stop_apps([]).
|
||||
|
||||
t_ws_will(_Config) ->
|
||||
{ok, ClientPid} = emqx_client:start_link(),
|
||||
{ok, _} = emqx_client:connect(ClientPid),
|
||||
{ok, _, [1]} = emqx_client:subscribe(ClientPid, ?WILL_TOPIC, qos1),
|
||||
WS = rfc6455_client:new("ws://127.0.0.1:8083" ++ "/mqtt", self()),
|
||||
{ok, _} = rfc6455_client:open(WS),
|
||||
Packet = raw_send_serialize(?WILL_CLIENT),
|
||||
ok = rfc6455_client:send_binary(WS, Packet),
|
||||
{binary, Bin} = rfc6455_client:recv(WS),
|
||||
Connack = ?CONNACK_PACKET(?CONNACK_ACCEPT),
|
||||
{ok, Connack, <<>>, _} = raw_recv_pase(Bin),
|
||||
exit(WS, abnomal),
|
||||
?assertEqual(1, length(emqx_client_SUITE:receive_messages(1))),
|
||||
ok = emqx_client:disconnect(ClientPid),
|
||||
ok.
|
||||
|
||||
t_ws_auth_failure(_Config) ->
|
||||
application:set_env(emqx, allow_anonymous, false),
|
||||
WS = rfc6455_client:new("ws://127.0.0.1:8083" ++ "/mqtt", self()),
|
||||
{ok, _} = rfc6455_client:open(WS),
|
||||
Connect = ?CONNECT_PACKET(
|
||||
#mqtt_packet_connect{
|
||||
client_id = <<"mqtt_client">>,
|
||||
username = <<"admin">>,
|
||||
password = <<"public">>
|
||||
}),
|
||||
ok = rfc6455_client:send_binary(WS, raw_send_serialize(Connect)),
|
||||
{binary, Bin} = rfc6455_client:recv(WS),
|
||||
Connack = ?CONNACK_PACKET(?CONNACK_ACCEPT),
|
||||
{ok, Connack, <<>>, _} = raw_recv_pase(Bin),
|
||||
Pid = emqx_cm:lookup_conn_pid(<<"mqtt_client">>),
|
||||
ConnInfo = emqx_ws_channel:info(Pid),
|
||||
ok = t_info(ConnInfo),
|
||||
ConnAttrs = emqx_ws_channel:attrs(Pid),
|
||||
ok = t_attrs(ConnAttrs),
|
||||
ConnStats = emqx_ws_channel:stats(Pid),
|
||||
ok = t_stats(ConnStats),
|
||||
SessionPid = emqx_ws_channel:session(Pid),
|
||||
true = is_pid(SessionPid),
|
||||
ok = emqx_ws_channel:kick(Pid),
|
||||
{close, _} = rfc6455_client:close(WS),
|
||||
ok.
|
||||
|
||||
t_ws_other_type_frame(_Config) ->
|
||||
WS = rfc6455_client:new("ws://127.0.0.1:8083" ++ "/mqtt", self()),
|
||||
{ok, _} = rfc6455_client:open(WS),
|
||||
Connect = ?CONNECT_PACKET(
|
||||
#mqtt_packet_connect{
|
||||
client_id = <<"mqtt_client">>,
|
||||
username = <<"admin">>,
|
||||
password = <<"public">>
|
||||
}),
|
||||
ok = rfc6455_client:send_binary(WS, raw_send_serialize(Connect)),
|
||||
{binary, Bin} = rfc6455_client:recv(WS),
|
||||
Connack = ?CONNACK_PACKET(?CONNACK_ACCEPT),
|
||||
{ok, Connack, <<>>, _} = raw_recv_pase(Bin),
|
||||
rfc6455_client:send(WS, <<"testdata">>),
|
||||
timer:sleep(1000),
|
||||
?assertEqual(undefined, erlang:process_info(WS)),
|
||||
ok.
|
||||
|
||||
raw_send_serialize(Packet) ->
|
||||
emqx_frame:serialize(Packet).
|
||||
|
||||
raw_recv_pase(Packet) ->
|
||||
emqx_frame:parse(Packet).
|
||||
|
||||
t_info(InfoData) ->
|
||||
?assertEqual(websocket, maps:get(socktype, InfoData)),
|
||||
?assertEqual(running, maps:get(conn_state, InfoData)),
|
||||
?assertEqual(<<"mqtt_client">>, maps:get(client_id, InfoData)),
|
||||
?assertEqual(<<"admin">>, maps:get(username, InfoData)),
|
||||
?assertEqual(<<"MQTT">>, maps:get(proto_name, InfoData)).
|
||||
|
||||
t_attrs(AttrsData) ->
|
||||
?assertEqual(<<"mqtt_client">>, maps:get(client_id, AttrsData)),
|
||||
?assertEqual(emqx_ws_channel, maps:get(conn_mod, AttrsData)),
|
||||
?assertEqual(<<"admin">>, maps:get(username, AttrsData)).
|
||||
|
||||
t_stats(StatsData) ->
|
||||
?assertEqual(true, proplists:get_value(recv_oct, StatsData) >= 0),
|
||||
?assertEqual(true, proplists:get_value(mailbox_len, StatsData) >= 0),
|
||||
?assertEqual(true, proplists:get_value(heap_size, StatsData) >= 0),
|
||||
?assertEqual(true, proplists:get_value(reductions, StatsData) >=0),
|
||||
?assertEqual(true, proplists:get_value(recv_pkt, StatsData) =:=1),
|
||||
?assertEqual(true, proplists:get_value(recv_msg, StatsData) >=0),
|
||||
?assertEqual(true, proplists:get_value(send_pkt, StatsData) =:=1).
|
||||
|
|
@ -19,20 +19,31 @@
|
|||
-compile(export_all).
|
||||
-compile(nowarn_export_all).
|
||||
|
||||
-include("emqx_mqtt.hrl").
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
all() -> [t_set_get_env].
|
||||
-define(OPTS, [{enable_acl, true},
|
||||
{enable_banned, false}
|
||||
]).
|
||||
|
||||
all() -> emqx_ct:all(?MODULE).
|
||||
|
||||
t_set_get_env(_) ->
|
||||
application:set_env(emqx, zones, [{china, [{language, chinese}]}]),
|
||||
_ = application:load(emqx),
|
||||
application:set_env(emqx, zones, [{external, ?OPTS}]),
|
||||
{ok, _} = emqx_zone:start_link(),
|
||||
chinese = emqx_zone:get_env(china, language),
|
||||
cn470 = emqx_zone:get_env(china, ism_band, cn470),
|
||||
undefined = emqx_zone:get_env(undefined, delay),
|
||||
500 = emqx_zone:get_env(undefined, delay, 500),
|
||||
application:set_env(emqx, zones, [{zone1, [{key, val}]}]),
|
||||
?assertEqual(undefined, emqx_zone:get_env(zone1, key)),
|
||||
emqx_zone:force_reload(),
|
||||
?assertEqual(val, emqx_zone:get_env(zone1, key)),
|
||||
?assert(emqx_zone:get_env(external, enable_acl)),
|
||||
?assertNot(emqx_zone:get_env(external, enable_banned)),
|
||||
?assertEqual(defval, emqx_zone:get_env(extenal, key, defval)),
|
||||
?assertEqual(undefined, emqx_zone:get_env(external, key)),
|
||||
?assertEqual(undefined, emqx_zone:get_env(internal, key)),
|
||||
?assertEqual(def, emqx_zone:get_env(internal, key, def)),
|
||||
emqx_zone:stop().
|
||||
|
||||
t_force_reload(_) ->
|
||||
{ok, _} = emqx_zone:start_link(),
|
||||
application:set_env(emqx, zones, [{zone, [{key, val}]}]),
|
||||
?assertEqual(undefined, emqx_zone:get_env(zone, key)),
|
||||
ok = emqx_zone:force_reload(),
|
||||
?assertEqual(val, emqx_zone:get_env(zone, key)),
|
||||
emqx_zone:stop().
|
||||
|
||||
|
|
|
@ -1,251 +0,0 @@
|
|||
%% The contents of this file are subject to the Mozilla Public License
|
||||
%% Version 1.1 (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.mozilla.org/MPL/
|
||||
%%
|
||||
%% Software distributed under the License is distributed on an "AS IS"
|
||||
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
|
||||
%% License for the specific language governing rights and limitations
|
||||
%% under the License.
|
||||
%%
|
||||
%% The Original Code is RabbitMQ Management Console.
|
||||
%%
|
||||
%% The Initial Developer of the Original Code is GoPivotal, Inc.
|
||||
%% Copyright (c) 2012-2016 Pivotal Software, Inc. All rights reserved.
|
||||
%%
|
||||
|
||||
-module(rfc6455_client).
|
||||
|
||||
-export([new/2, open/1, recv/1, send/2, send_binary/2, close/1, close/2]).
|
||||
|
||||
-record(state, {host, port, addr, path, ppid, socket, data, phase}).
|
||||
|
||||
%% --------------------------------------------------------------------------
|
||||
|
||||
new(WsUrl, PPid) ->
|
||||
crypto:start(),
|
||||
"ws://" ++ Rest = WsUrl,
|
||||
[Addr, Path] = split("/", Rest, 1),
|
||||
[Host, MaybePort] = split(":", Addr, 1, empty),
|
||||
Port = case MaybePort of
|
||||
empty -> 80;
|
||||
V -> {I, ""} = string:to_integer(V), I
|
||||
end,
|
||||
State = #state{host = Host,
|
||||
port = Port,
|
||||
addr = Addr,
|
||||
path = "/" ++ Path,
|
||||
ppid = PPid},
|
||||
spawn(fun() ->
|
||||
start_conn(State)
|
||||
end).
|
||||
|
||||
open(WS) ->
|
||||
receive
|
||||
{rfc6455, open, WS, Opts} ->
|
||||
{ok, Opts};
|
||||
{rfc6455, close, WS, R} ->
|
||||
{close, R}
|
||||
end.
|
||||
|
||||
recv(WS) ->
|
||||
receive
|
||||
{rfc6455, recv, WS, Payload} ->
|
||||
{ok, Payload};
|
||||
{rfc6455, recv_binary, WS, Payload} ->
|
||||
{binary, Payload};
|
||||
{rfc6455, close, WS, R} ->
|
||||
{close, R}
|
||||
end.
|
||||
|
||||
send(WS, IoData) ->
|
||||
WS ! {send, IoData},
|
||||
ok.
|
||||
|
||||
send_binary(WS, IoData) ->
|
||||
WS ! {send_binary, IoData},
|
||||
ok.
|
||||
|
||||
close(WS) ->
|
||||
close(WS, {1000, ""}).
|
||||
|
||||
close(WS, WsReason) ->
|
||||
WS ! {close, WsReason},
|
||||
receive
|
||||
{rfc6455, close, WS, R} ->
|
||||
{close, R}
|
||||
end.
|
||||
|
||||
|
||||
%% --------------------------------------------------------------------------
|
||||
|
||||
start_conn(State) ->
|
||||
{ok, Socket} = gen_tcp:connect(State#state.host, State#state.port,
|
||||
[binary,
|
||||
{packet, 0}]),
|
||||
Key = base64:encode_to_string(crypto:strong_rand_bytes(16)),
|
||||
gen_tcp:send(Socket,
|
||||
"GET " ++ State#state.path ++ " HTTP/1.1\r\n" ++
|
||||
"Host: " ++ State#state.addr ++ "\r\n" ++
|
||||
"Upgrade: websocket\r\n" ++
|
||||
"Connection: Upgrade\r\n" ++
|
||||
"Sec-WebSocket-Key: " ++ Key ++ "\r\n" ++
|
||||
"Origin: null\r\n" ++
|
||||
"Sec-WebSocket-Protocol: mqtt\r\n" ++
|
||||
"Sec-WebSocket-Version: 13\r\n\r\n"),
|
||||
|
||||
loop(State#state{socket = Socket,
|
||||
data = <<>>,
|
||||
phase = opening}).
|
||||
|
||||
do_recv(State = #state{phase = opening, ppid = PPid, data = Data}) ->
|
||||
case split("\r\n\r\n", binary_to_list(Data), 1, empty) of
|
||||
[_Http, empty] -> State;
|
||||
[Http, Data1] ->
|
||||
%% TODO: don't ignore http response data, verify key
|
||||
PPid ! {rfc6455, open, self(), [{http_response, Http}]},
|
||||
State#state{phase = open,
|
||||
data = Data1}
|
||||
end;
|
||||
do_recv(State = #state{phase = Phase, data = Data, socket = Socket, ppid = PPid})
|
||||
when Phase =:= open orelse Phase =:= closing ->
|
||||
R = case Data of
|
||||
<<F:1, _:3, O:4, 0:1, L:7, Payload:L/binary, Rest/binary>>
|
||||
when L < 126 ->
|
||||
{F, O, Payload, Rest};
|
||||
|
||||
<<F:1, _:3, O:4, 0:1, 126:7, L2:16, Payload:L2/binary, Rest/binary>> ->
|
||||
{F, O, Payload, Rest};
|
||||
|
||||
<<F:1, _:3, O:4, 0:1, 127:7, L2:64, Payload:L2/binary, Rest/binary>> ->
|
||||
{F, O, Payload, Rest};
|
||||
|
||||
<<_:1, _:3, _:4, 1:1, _/binary>> ->
|
||||
%% According o rfc6455 5.1 the server must not mask any frames.
|
||||
die(Socket, PPid, {1006, "Protocol error"}, normal);
|
||||
_ ->
|
||||
moredata
|
||||
end,
|
||||
case R of
|
||||
moredata ->
|
||||
State;
|
||||
_ -> do_recv2(State, R)
|
||||
end.
|
||||
|
||||
do_recv2(State = #state{phase = Phase, socket = Socket, ppid = PPid}, R) ->
|
||||
case R of
|
||||
{1, 1, Payload, Rest} ->
|
||||
PPid ! {rfc6455, recv, self(), Payload},
|
||||
State#state{data = Rest};
|
||||
{1, 2, Payload, Rest} ->
|
||||
PPid ! {rfc6455, recv_binary, self(), Payload},
|
||||
State#state{data = Rest};
|
||||
{1, 8, Payload, _Rest} ->
|
||||
WsReason = case Payload of
|
||||
<<WC:16, WR/binary>> -> {WC, WR};
|
||||
<<>> -> {1005, "No status received"}
|
||||
end,
|
||||
case Phase of
|
||||
open -> %% echo
|
||||
do_close(State, WsReason),
|
||||
gen_tcp:close(Socket);
|
||||
closing ->
|
||||
ok
|
||||
end,
|
||||
die(Socket, PPid, WsReason, normal);
|
||||
{_, _, _, _Rest2} ->
|
||||
io:format("Unknown frame type~n"),
|
||||
die(Socket, PPid, {1006, "Unknown frame type"}, normal)
|
||||
end.
|
||||
|
||||
encode_frame(F, O, Payload) ->
|
||||
Mask = crypto:strong_rand_bytes(4),
|
||||
MaskedPayload = apply_mask(Mask, iolist_to_binary(Payload)),
|
||||
|
||||
L = byte_size(MaskedPayload),
|
||||
IoData = case L of
|
||||
_ when L < 126 ->
|
||||
[<<F:1, 0:3, O:4, 1:1, L:7>>, Mask, MaskedPayload];
|
||||
_ when L < 65536 ->
|
||||
[<<F:1, 0:3, O:4, 1:1, 126:7, L:16>>, Mask, MaskedPayload];
|
||||
_ ->
|
||||
[<<F:1, 0:3, O:4, 1:1, 127:7, L:64>>, Mask, MaskedPayload]
|
||||
end,
|
||||
iolist_to_binary(IoData).
|
||||
|
||||
do_send(State = #state{socket = Socket}, Payload) ->
|
||||
gen_tcp:send(Socket, encode_frame(1, 1, Payload)),
|
||||
State.
|
||||
|
||||
do_send_binary(State = #state{socket = Socket}, Payload) ->
|
||||
gen_tcp:send(Socket, encode_frame(1, 2, Payload)),
|
||||
State.
|
||||
|
||||
do_close(State = #state{socket = Socket}, {Code, Reason}) ->
|
||||
Payload = iolist_to_binary([<<Code:16>>, Reason]),
|
||||
gen_tcp:send(Socket, encode_frame(1, 8, Payload)),
|
||||
State#state{phase = closing}.
|
||||
|
||||
loop(State = #state{socket = Socket, ppid = PPid, data = Data,
|
||||
phase = Phase}) ->
|
||||
receive
|
||||
{tcp, Socket, Bin} ->
|
||||
State1 = State#state{data = iolist_to_binary([Data, Bin])},
|
||||
loop(do_recv(State1));
|
||||
{send, Payload} when Phase == open ->
|
||||
loop(do_send(State, Payload));
|
||||
{send_binary, Payload} when Phase == open ->
|
||||
loop(do_send_binary(State, Payload));
|
||||
{tcp_closed, Socket} ->
|
||||
die(Socket, PPid, {1006, "Connection closed abnormally"}, normal);
|
||||
{close, WsReason} when Phase == open ->
|
||||
loop(do_close(State, WsReason))
|
||||
end.
|
||||
|
||||
|
||||
die(Socket, PPid, WsReason, Reason) ->
|
||||
gen_tcp:shutdown(Socket, read_write),
|
||||
PPid ! {rfc6455, close, self(), WsReason},
|
||||
exit(Reason).
|
||||
|
||||
|
||||
%% --------------------------------------------------------------------------
|
||||
|
||||
split(SubStr, Str, Limit) ->
|
||||
split(SubStr, Str, Limit, "").
|
||||
|
||||
split(SubStr, Str, Limit, Default) ->
|
||||
Acc = split(SubStr, Str, Limit, [], Default),
|
||||
lists:reverse(Acc).
|
||||
split(_SubStr, Str, 0, Acc, _Default) -> [Str | Acc];
|
||||
split(SubStr, Str, Limit, Acc, Default) ->
|
||||
{L, R} = case string:str(Str, SubStr) of
|
||||
0 -> {Str, Default};
|
||||
I -> {string:substr(Str, 1, I-1),
|
||||
string:substr(Str, I+length(SubStr))}
|
||||
end,
|
||||
split(SubStr, R, Limit-1, [L | Acc], Default).
|
||||
|
||||
|
||||
apply_mask(Mask, Data) when is_number(Mask) ->
|
||||
apply_mask(<<Mask:32>>, Data);
|
||||
|
||||
apply_mask(<<0:32>>, Data) ->
|
||||
Data;
|
||||
apply_mask(Mask, Data) ->
|
||||
iolist_to_binary(lists:reverse(apply_mask2(Mask, Data, []))).
|
||||
|
||||
apply_mask2(M = <<Mask:32>>, <<Data:32, Rest/binary>>, Acc) ->
|
||||
T = Data bxor Mask,
|
||||
apply_mask2(M, Rest, [<<T:32>> | Acc]);
|
||||
apply_mask2(<<Mask:24, _:8>>, <<Data:24>>, Acc) ->
|
||||
T = Data bxor Mask,
|
||||
[<<T:24>> | Acc];
|
||||
apply_mask2(<<Mask:16, _:16>>, <<Data:16>>, Acc) ->
|
||||
T = Data bxor Mask,
|
||||
[<<T:16>> | Acc];
|
||||
apply_mask2(<<Mask:8, _:24>>, <<Data:8>>, Acc) ->
|
||||
T = Data bxor Mask,
|
||||
[<<T:8>> | Acc];
|
||||
apply_mask2(_, <<>>, Acc) ->
|
||||
Acc.
|
|
@ -1,75 +0,0 @@
|
|||
-module(ws_client).
|
||||
|
||||
-export([
|
||||
start_link/0,
|
||||
start_link/1,
|
||||
send_binary/2,
|
||||
send_ping/2,
|
||||
recv/2,
|
||||
recv/1,
|
||||
stop/1
|
||||
]).
|
||||
|
||||
-export([
|
||||
init/2,
|
||||
websocket_handle/3,
|
||||
websocket_info/3,
|
||||
websocket_terminate/3
|
||||
]).
|
||||
|
||||
-record(state, {
|
||||
buffer = [] :: list(),
|
||||
waiting = undefined :: undefined | pid()
|
||||
}).
|
||||
|
||||
start_link() ->
|
||||
start_link("ws://localhost:8083/mqtt").
|
||||
|
||||
start_link(Url) ->
|
||||
websocket_client:start_link(Url, ?MODULE, [], [{extra_headers, [{"Sec-Websocket-Protocol", "mqtt"}]}]).
|
||||
|
||||
stop(Pid) ->
|
||||
Pid ! stop.
|
||||
|
||||
send_binary(Pid, Msg) ->
|
||||
websocket_client:cast(Pid, {binary, Msg}).
|
||||
|
||||
send_ping(Pid, Msg) ->
|
||||
websocket_client:cast(Pid, {ping, Msg}).
|
||||
|
||||
recv(Pid) ->
|
||||
recv(Pid, 5000).
|
||||
|
||||
recv(Pid, Timeout) ->
|
||||
Pid ! {recv, self()},
|
||||
receive
|
||||
M -> M
|
||||
after
|
||||
Timeout -> error
|
||||
end.
|
||||
|
||||
init(_, _WSReq) ->
|
||||
{ok, #state{}}.
|
||||
|
||||
websocket_handle(Frame, _, State = #state{waiting = undefined, buffer = Buffer}) ->
|
||||
logger:info("Client received frame~p", [Frame]),
|
||||
{ok, State#state{buffer = [Frame|Buffer]}};
|
||||
websocket_handle(Frame, _, State = #state{waiting = From}) ->
|
||||
logger:info("Client received frame~p", [Frame]),
|
||||
From ! Frame,
|
||||
{ok, State#state{waiting = undefined}}.
|
||||
|
||||
websocket_info({send_text, Text}, WSReq, State) ->
|
||||
websocket_client:send({text, Text}, WSReq),
|
||||
{ok, State};
|
||||
websocket_info({recv, From}, _, State = #state{buffer = []}) ->
|
||||
{ok, State#state{waiting = From}};
|
||||
websocket_info({recv, From}, _, State = #state{buffer = [Top|Rest]}) ->
|
||||
From ! Top,
|
||||
{ok, State#state{buffer = Rest}};
|
||||
websocket_info(stop, _, State) ->
|
||||
{close, <<>>, State}.
|
||||
|
||||
websocket_terminate(Close, _, State) ->
|
||||
io:format("Websocket closed with frame ~p and state ~p", [Close, State]),
|
||||
ok.
|
Loading…
Reference in New Issue