Fix issue#1293 - the retained flags should be propagated for bridge.

This commit is contained in:
Feng Lee 2017-11-29 14:09:46 +08:00
parent 2611f660de
commit 26fb809dbe
4 changed files with 32 additions and 23 deletions

View File

@ -174,7 +174,8 @@
will_topic = undefined :: undefined | binary(), will_topic = undefined :: undefined | binary(),
will_msg = undefined :: undefined | binary(), will_msg = undefined :: undefined | binary(),
username = undefined :: undefined | binary(), username = undefined :: undefined | binary(),
password = undefined :: undefined | binary() password = undefined :: undefined | binary(),
is_bridge = false :: boolean()
}). }).
-record(mqtt_packet_connack, -record(mqtt_packet_connack,

View File

@ -79,7 +79,7 @@ parse_frame(Bin, #mqtt_packet_header{type = Type, qos = Qos} = Header, Length)
{?CONNECT, <<FrameBin:Length/binary, Rest/binary>>} -> {?CONNECT, <<FrameBin:Length/binary, Rest/binary>>} ->
{ProtoName, Rest1} = parse_utf(FrameBin), {ProtoName, Rest1} = parse_utf(FrameBin),
%% Fix mosquitto bridge: 0x83, 0x84 %% Fix mosquitto bridge: 0x83, 0x84
<<_Bridge:4, ProtoVersion:4, Rest2/binary>> = Rest1, <<BridgeTag:4, ProtoVersion:4, Rest2/binary>> = Rest1,
<<UsernameFlag : 1, <<UsernameFlag : 1,
PasswordFlag : 1, PasswordFlag : 1,
WillRetain : 1, WillRetain : 1,
@ -109,7 +109,8 @@ parse_frame(Bin, #mqtt_packet_header{type = Type, qos = Qos} = Header, Length)
will_topic = WillTopic, will_topic = WillTopic,
will_msg = WillMsg, will_msg = WillMsg,
username = UserName, username = UserName,
password = PasssWord}, Rest); password = PasssWord,
is_bridge = (BridgeTag =:= 8)}, Rest);
false -> false ->
{error, protocol_header_corrupt} {error, protocol_header_corrupt}
end; end;

View File

@ -44,7 +44,7 @@
clean_sess, proto_ver, proto_name, username, is_superuser, clean_sess, proto_ver, proto_name, username, is_superuser,
will_msg, keepalive, keepalive_backoff, max_clientid_len, will_msg, keepalive, keepalive_backoff, max_clientid_len,
session, stats_data, mountpoint, ws_initial_headers, session, stats_data, mountpoint, ws_initial_headers,
connected_at}). is_bridge, connected_at}).
-type(proto_state() :: #proto_state{}). -type(proto_state() :: #proto_state{}).
@ -180,7 +180,8 @@ process(?CONNECT_PACKET(Var), State0) ->
password = Password, password = Password,
clean_sess = CleanSess, clean_sess = CleanSess,
keep_alive = KeepAlive, keep_alive = KeepAlive,
client_id = ClientId} = Var, client_id = ClientId,
is_bridge = IsBridge} = Var,
State1 = State0#proto_state{proto_ver = ProtoVer, State1 = State0#proto_state{proto_ver = ProtoVer,
proto_name = ProtoName, proto_name = ProtoName,
@ -189,6 +190,7 @@ process(?CONNECT_PACKET(Var), State0) ->
clean_sess = CleanSess, clean_sess = CleanSess,
keepalive = KeepAlive, keepalive = KeepAlive,
will_msg = willmsg(Var, State0), will_msg = willmsg(Var, State0),
is_bridge = IsBridge,
connected_at = os:timestamp()}, connected_at = os:timestamp()},
{ReturnCode1, SessPresent, State3} = {ReturnCode1, SessPresent, State3} =
@ -333,10 +335,11 @@ with_puback(Type, Packet = ?PUBLISH_PACKET(_Qos, PacketId),
-spec(send(mqtt_message() | mqtt_packet(), proto_state()) -> {ok, proto_state()}). -spec(send(mqtt_message() | mqtt_packet(), proto_state()) -> {ok, proto_state()}).
send(Msg, State = #proto_state{client_id = ClientId, send(Msg, State = #proto_state{client_id = ClientId,
username = Username, username = Username,
mountpoint = MountPoint}) mountpoint = MountPoint,
is_bridge = IsBridge})
when is_record(Msg, mqtt_message) -> when is_record(Msg, mqtt_message) ->
emqttd_hooks:run('message.delivered', [ClientId, Username], Msg), emqttd_hooks:run('message.delivered', [ClientId, Username], Msg),
send(emqttd_message:to_packet(unmount(MountPoint, Msg)), State); send(emqttd_message:to_packet(unmount(MountPoint, clean_retain(IsBridge, Msg))), State);
send(Packet = ?PACKET(Type), send(Packet = ?PACKET(Type),
State = #proto_state{sendfun = SendFun, stats_data = Stats}) -> State = #proto_state{sendfun = SendFun, stats_data = Stats}) ->
@ -543,6 +546,15 @@ check_acl(subscribe, Topic, Client) ->
sp(true) -> 1; sp(true) -> 1;
sp(false) -> 0. sp(false) -> 0.
%%--------------------------------------------------------------------
%% The retained flag should be propagated for bridge.
%%--------------------------------------------------------------------
clean_retain(false, Msg = #mqtt_message{retain = true}) ->
Msg#mqtt_message{retain = false};
clean_retain(true, Msg) ->
Msg.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% Mount Point %% Mount Point
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------

View File

@ -152,9 +152,10 @@
%% Force GC Count %% Force GC Count
force_gc_count :: undefined | integer(), force_gc_count :: undefined | integer(),
created_at :: erlang:timestamp(), %% Ignore loop deliver?
ignore_loop_deliver = false :: boolean(),
ignore_loop_deliver = false :: boolean() created_at :: erlang:timestamp()
}). }).
-define(TIMEOUT, 60000). -define(TIMEOUT, 60000).
@ -529,17 +530,14 @@ handle_cast({destroy, ClientId},
handle_cast(Msg, State) -> handle_cast(Msg, State) ->
?UNEXPECTED_MSG(Msg, State). ?UNEXPECTED_MSG(Msg, State).
%% Dispatch message from self publish %% Ignore Messages delivered by self
handle_info({dispatch, Topic, Msg = #mqtt_message{from = {ClientId, _}}}, handle_info({dispatch, _Topic, #mqtt_message{from = {ClientId, _}}},
State = #state{client_id = ClientId, State = #state{client_id = ClientId, ignore_loop_deliver = true}) ->
ignore_loop_deliver = IgnoreLoopDeliver}) when is_record(Msg, mqtt_message) -> hibernate(State);
case IgnoreLoopDeliver of
true -> {noreply, State, hibernate};
false -> {noreply, handle_dispatch(Topic, Msg, State), hibernate}
end;
%% Dispatch Message %% Dispatch Message
handle_info({dispatch, Topic, Msg}, State) when is_record(Msg, mqtt_message) -> handle_info({dispatch, Topic, Msg}, State) when is_record(Msg, mqtt_message) ->
{noreply, handle_dispatch(Topic, Msg, State), hibernate}; hibernate(gc(dispatch(tune_qos(Topic, reset_dup(Msg), State), State)));
%% Do nothing if the client has been disconnected. %% Do nothing if the client has been disconnected.
handle_info({timeout, _Timer, retry_delivery}, State = #state{client_pid = undefined}) -> handle_info({timeout, _Timer, retry_delivery}, State = #state{client_pid = undefined}) ->
@ -552,7 +550,7 @@ handle_info({timeout, _Timer, check_awaiting_rel}, State) ->
hibernate(expire_awaiting_rel(emit_stats(State#state{await_rel_timer = undefined}))); hibernate(expire_awaiting_rel(emit_stats(State#state{await_rel_timer = undefined})));
handle_info({timeout, _Timer, expired}, State) -> handle_info({timeout, _Timer, expired}, State) ->
?LOG(debug, "Expired, shutdown now.", [], State), ?LOG(info, "Expired, shutdown now.", [], State),
shutdown(expired, State); shutdown(expired, State);
handle_info({'EXIT', ClientPid, _Reason}, handle_info({'EXIT', ClientPid, _Reason},
@ -563,7 +561,7 @@ handle_info({'EXIT', ClientPid, Reason},
State = #state{clean_sess = false, State = #state{clean_sess = false,
client_pid = ClientPid, client_pid = ClientPid,
expiry_interval = Interval}) -> expiry_interval = Interval}) ->
?LOG(debug, "Client ~p EXIT for ~p", [ClientPid, Reason], State), ?LOG(info, "Client ~p EXIT for ~p", [ClientPid, Reason], State),
ExpireTimer = start_timer(Interval, expired), ExpireTimer = start_timer(Interval, expired),
State1 = State#state{client_pid = undefined, expiry_timer = ExpireTimer}, State1 = State#state{client_pid = undefined, expiry_timer = ExpireTimer},
hibernate(emit_stats(State1)); hibernate(emit_stats(State1));
@ -687,9 +685,6 @@ is_awaiting_full(#state{awaiting_rel = AwaitingRel, max_awaiting_rel = MaxLen})
%% Dispatch Messages %% Dispatch Messages
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
handle_dispatch(Topic, Msg, State) ->
gc(dispatch(tune_qos(Topic, reset_dup(Msg), State), State)).
%% Enqueue message if the client has been disconnected %% Enqueue message if the client has been disconnected
dispatch(Msg, State = #state{client_pid = undefined}) -> dispatch(Msg, State = #state{client_pid = undefined}) ->
enqueue_msg(Msg, State); enqueue_msg(Msg, State);