Fix issue#1874 (#1964)
* Fix issue#1874 Prior to this change, if user use one client connect emqx with mqtt v3.1.1, the client subscribe the topic and publish message to this topic, it would receive this message itself published, this commit provide a configure option to let user ignore the message itself published. This change fix issue 1874. * Small Fix * Fix bug * Better design * Fix compile warning and improve coverage * Better design to solve the performance issue * Fix typo * Fix typo * Delete spaces in end of lines. * Do not use anonymous function * Better performance
This commit is contained in:
parent
bc1464a33f
commit
16821490ce
|
@ -474,6 +474,11 @@ mqtt.wildcard_subscription = true
|
|||
## Value: boolean
|
||||
mqtt.shared_subscription = true
|
||||
|
||||
## Whether to ignore loop delivery of messages.(for mqtt v3.1.1)
|
||||
##
|
||||
## Value: true | false
|
||||
mqtt.ignore_loop_deliver = false
|
||||
|
||||
##--------------------------------------------------------------------
|
||||
## Zones
|
||||
##--------------------------------------------------------------------
|
||||
|
|
|
@ -595,6 +595,12 @@ end}.
|
|||
{datatype, {enum, [true, false]}}
|
||||
]}.
|
||||
|
||||
%% @doc Whether to ignore loop delivery of messages.(for mqtt v3.1.1)
|
||||
{mapping, "mqtt.ignore_loop_deliver", "emqx.mqtt_ignore_loop_deliver", [
|
||||
{default, true},
|
||||
{datatype, {enum, [true, false]}}
|
||||
]}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Zones
|
||||
%%--------------------------------------------------------------------
|
||||
|
|
|
@ -201,7 +201,7 @@ aggre(Routes) ->
|
|||
lists:foldl(
|
||||
fun(#route{topic = To, dest = Node}, Acc) when is_atom(Node) ->
|
||||
[{To, Node} | Acc];
|
||||
(#route{topic = To, dest = {Group, _Node}}, Acc) ->
|
||||
(#route{topic = To, dest = {Group, _Node}}, Acc) ->
|
||||
lists:usort([{To, Group} | Acc])
|
||||
end, [], Routes).
|
||||
|
||||
|
|
|
@ -63,7 +63,8 @@
|
|||
recv_stats,
|
||||
send_stats,
|
||||
connected,
|
||||
connected_at
|
||||
connected_at,
|
||||
ignore_loop
|
||||
}).
|
||||
|
||||
-type(state() :: #pstate{}).
|
||||
|
@ -71,6 +72,7 @@
|
|||
|
||||
-ifdef(TEST).
|
||||
-compile(export_all).
|
||||
-compile(nowarn_export_all).
|
||||
-endif.
|
||||
|
||||
-define(NO_PROPS, undefined).
|
||||
|
@ -102,7 +104,8 @@ init(#{peername := Peername, peercert := Peercert, sendfun := SendFun}, Options)
|
|||
enable_acl = emqx_zone:get_env(Zone, enable_acl),
|
||||
recv_stats = #{msg => 0, pkt => 0},
|
||||
send_stats = #{msg => 0, pkt => 0},
|
||||
connected = false}.
|
||||
connected = false,
|
||||
ignore_loop = emqx_config:get_env(mqtt_ignore_loop_deliver, false)}.
|
||||
|
||||
init_username(Peercert, Options) ->
|
||||
case proplists:get_value(peer_cert_as_username, Options) of
|
||||
|
@ -385,11 +388,14 @@ process_packet(?PUBCOMP_PACKET(PacketId, ReasonCode), PState = #pstate{session =
|
|||
{ok = emqx_session:pubcomp(SPid, PacketId, ReasonCode), PState};
|
||||
|
||||
process_packet(?SUBSCRIBE_PACKET(PacketId, Properties, RawTopicFilters),
|
||||
PState = #pstate{session = SPid, mountpoint = Mountpoint, proto_ver = ProtoVer, is_bridge = IsBridge}) ->
|
||||
PState = #pstate{session = SPid, mountpoint = Mountpoint,
|
||||
proto_ver = ProtoVer, is_bridge = IsBridge,
|
||||
ignore_loop = IgnoreLoop}) ->
|
||||
RawTopicFilters1 = if ProtoVer < ?MQTT_PROTO_V5 ->
|
||||
IfIgnoreLoop = case IgnoreLoop of true -> 1; false -> 0 end,
|
||||
case IsBridge of
|
||||
true -> [{RawTopic, SubOpts#{rap => 1}} || {RawTopic, SubOpts} <- RawTopicFilters];
|
||||
false -> [{RawTopic, SubOpts#{rap => 0}} || {RawTopic, SubOpts} <- RawTopicFilters]
|
||||
true -> [{RawTopic, SubOpts#{rap => 1, nl => IfIgnoreLoop}} || {RawTopic, SubOpts} <- RawTopicFilters];
|
||||
false -> [{RawTopic, SubOpts#{rap => 0, nl => IfIgnoreLoop}} || {RawTopic, SubOpts} <- RawTopicFilters]
|
||||
end;
|
||||
true ->
|
||||
RawTopicFilters
|
||||
|
@ -626,7 +632,6 @@ try_open_session(PState = #pstate{zone = Zone,
|
|||
clean_start => CleanStart,
|
||||
will_msg => WillMsg
|
||||
},
|
||||
|
||||
SessAttrs1 = lists:foldl(fun set_session_attrs/2, SessAttrs, [{max_inflight, PState}, {expiry_interval, PState}, {topic_alias_maximum, PState}]),
|
||||
case emqx_sm:open_session(SessAttrs1) of
|
||||
{ok, SPid} ->
|
||||
|
|
|
@ -152,6 +152,7 @@
|
|||
will_msg :: emqx:message(),
|
||||
|
||||
will_delay_timer :: reference() | undefined
|
||||
|
||||
}).
|
||||
|
||||
-type(spid() :: pid()).
|
||||
|
@ -575,7 +576,8 @@ handle_info({dispatch, Topic, Msgs}, State) when is_list(Msgs) ->
|
|||
|
||||
%% Dispatch message
|
||||
handle_info({dispatch, Topic, Msg = #message{headers = Headers}},
|
||||
State = #state{subscriptions = SubMap, topic_alias_maximum = TopicAliasMaximum}) when is_record(Msg, message) ->
|
||||
State = #state{subscriptions = SubMap,
|
||||
topic_alias_maximum = TopicAliasMaximum}) when is_record(Msg, message) ->
|
||||
TopicAlias = maps:get('Topic-Alias', Headers, undefined),
|
||||
if
|
||||
TopicAlias =:= undefined orelse TopicAlias =< TopicAliasMaximum ->
|
||||
|
@ -591,6 +593,7 @@ handle_info({dispatch, Topic, Msg = #message{headers = Headers}},
|
|||
noreply(State)
|
||||
end;
|
||||
|
||||
|
||||
%% Do nothing if the client has been disconnected.
|
||||
handle_info({timeout, Timer, retry_delivery}, State = #state{conn_pid = undefined, retry_timer = Timer}) ->
|
||||
noreply(State#state{retry_timer = undefined});
|
||||
|
|
|
@ -269,6 +269,7 @@ websocket_info(Info, State) ->
|
|||
terminate(SockError, _Req, #state{keepalive = Keepalive,
|
||||
proto_state = ProtoState,
|
||||
shutdown = Shutdown}) ->
|
||||
|
||||
?LOG(debug, "Terminated for ~p, sockerror: ~p",
|
||||
[Shutdown, SockError]),
|
||||
emqx_keepalive:cancel(Keepalive),
|
||||
|
|
|
@ -150,8 +150,7 @@ receive_messages(Count, Msgs) ->
|
|||
receive
|
||||
{publish, Msg} ->
|
||||
receive_messages(Count-1, [Msg|Msgs]);
|
||||
Other ->
|
||||
ct:log("~p~n", [Other]),
|
||||
_Other ->
|
||||
receive_messages(Count, Msgs)
|
||||
after 10 ->
|
||||
Msgs
|
||||
|
|
|
@ -18,9 +18,11 @@
|
|||
-compile(export_all).
|
||||
-compile(nowarn_export_all).
|
||||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
-include_lib("common_test/include/ct.hrl").
|
||||
|
||||
all() -> [t_session_all].
|
||||
all() -> [ignore_loop, t_session_all].
|
||||
|
||||
init_per_suite(Config) ->
|
||||
emqx_ct_broker_helpers:run_setup_steps(),
|
||||
|
@ -29,6 +31,19 @@ init_per_suite(Config) ->
|
|||
end_per_suite(_Config) ->
|
||||
emqx_ct_broker_helpers:run_teardown_steps().
|
||||
|
||||
ignore_loop(_Config) ->
|
||||
application:set_env(emqx, mqtt_ignore_loop_deliver, true),
|
||||
{ok, Client} = emqx_client:start_link(),
|
||||
{ok, _} = emqx_client:connect(Client),
|
||||
TestTopic = <<"Self">>,
|
||||
{ok, _, [2]} = emqx_client:subscribe(Client, TestTopic, qos2),
|
||||
ok = emqx_client:publish(Client, TestTopic, <<"testmsg">>, 0),
|
||||
{ok, _} = emqx_client:publish(Client, TestTopic, <<"testmsg">>, 1),
|
||||
{ok, _} = emqx_client:publish(Client, TestTopic, <<"testmsg">>, 2),
|
||||
?assertEqual(0, length(emqx_client_SUITE:receive_messages(3))),
|
||||
ok = emqx_client:disconnect(Client),
|
||||
application:set_env(emqx, mqtt_ignore_loop_deliver, false).
|
||||
|
||||
t_session_all(_) ->
|
||||
ClientId = <<"ClientId">>,
|
||||
{ok, ConnPid} = emqx_mock_client:start_link(ClientId),
|
||||
|
|
|
@ -28,7 +28,7 @@ t_open_close_session(_) ->
|
|||
client_id => <<"client">>,
|
||||
conn_pid => ClientPid,
|
||||
zone => internal,
|
||||
username => <<"zhou">>,
|
||||
username => <<"emqx">>,
|
||||
expiry_interval => 0,
|
||||
max_inflight => 0,
|
||||
topic_alias_maximum => 0,
|
||||
|
@ -47,4 +47,3 @@ t_open_close_session(_) ->
|
|||
ok = emqx_sm:close_session(SPid),
|
||||
[] = emqx_sm:lookup_session(<<"client">>),
|
||||
emqx_ct_broker_helpers:run_teardown_steps().
|
||||
|
||||
|
|
Loading…
Reference in New Issue