feat(mqttsn): support to register unknown topic-name to the client

This commit is contained in:
JianBo He 2022-03-02 14:57:35 +08:00
parent 5b1ba335cb
commit 7ade24b344
3 changed files with 80 additions and 30 deletions

View File

@ -338,9 +338,16 @@ process_connect(Channel = #channel{
SessFun SessFun
) of ) of
{ok, #{session := Session, {ok, #{session := Session,
present := _Present}} -> present := false}} ->
handle_out(connack, ?SN_RC_ACCEPTED, handle_out(connack, ?SN_RC_ACCEPTED,
Channel#channel{session = Session}); Channel#channel{session = Session});
{ok, #{session := Session, present := true, pendings := Pendings}} ->
Pendings1 = lists:usort(lists:append(Pendings, emqx_misc:drain_deliver())),
NChannel = Channel#channel{session = Session,
resuming = true,
pendings = Pendings1
},
handle_out(connack, ?SN_RC_ACCEPTED, NChannel);
{error, Reason} -> {error, Reason} ->
?SLOG(error, #{ msg => "failed_to_open_session" ?SLOG(error, #{ msg => "failed_to_open_session"
, reason => Reason , reason => Reason
@ -1101,10 +1108,11 @@ awake(Channel = #channel{session = Session, clientinfo = ClientInfo}) ->
{ok, More, Session2} -> {ok, More, Session2} ->
{lists:append(Publishes, More), Session2} {lists:append(Publishes, More), Session2}
end, end,
{Packets, NChannel} = do_deliver(NPublishes, {Replies, NChannel} = outgoing_deliver_and_register(
Channel#channel{session = NSession}), do_deliver(NPublishes,
Outgoing = [{outgoing, Packets} || length(Packets) > 0], Channel#channel{session = NSession})
{ok, Outgoing, NChannel}. ),
{ok, Replies, NChannel}.
asleep(Duration, Channel = #channel{conn_state = asleep}) -> asleep(Duration, Channel = #channel{conn_state = asleep}) ->
%% 6.14: The client can also modify its sleep duration %% 6.14: The client can also modify its sleep duration
@ -1146,8 +1154,10 @@ handle_out(connack, ReasonCode,
shutdown(Reason, AckPacket, Channel); shutdown(Reason, AckPacket, Channel);
handle_out(publish, Publishes, Channel) -> handle_out(publish, Publishes, Channel) ->
{Packets, NChannel} = do_deliver(Publishes, Channel), {Replies, NChannel} = outgoing_deliver_and_register(
{ok, {outgoing, Packets}, NChannel}; do_deliver(Publishes, Channel)
),
{ok, Replies, NChannel};
handle_out(puback, {TopicId, MsgId, Rc}, Channel) -> handle_out(puback, {TopicId, MsgId, Rc}, Channel) ->
{ok, {outgoing, ?SN_PUBACK_MSG(TopicId, MsgId, Rc)}, Channel}; {ok, {outgoing, ?SN_PUBACK_MSG(TopicId, MsgId, Rc)}, Channel};
@ -1207,17 +1217,18 @@ handle_out(disconnect, RC, Channel) ->
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
return_connack(AckPacket, Channel) -> return_connack(AckPacket, Channel) ->
Replies = [{event, connected}, {outgoing, AckPacket}], Replies1 = [{event, connected}, {outgoing, AckPacket}],
case maybe_resume_session(Channel) of case maybe_resume_session(Channel) of
ignore -> {ok, Replies, Channel}; ignore -> {ok, Replies1, Channel};
{ok, Publishes, NSession} -> {ok, Publishes, NSession} ->
NChannel = Channel#channel{session = NSession, NChannel = Channel#channel{session = NSession,
resuming = false, resuming = false,
pendings = [] pendings = []
}, },
{Packets, NChannel1} = do_deliver(Publishes, NChannel), {Replies2, NChannel1} = outgoing_deliver_and_register(
Outgoing = [{outgoing, Packets} || length(Packets) > 0], do_deliver(Publishes, NChannel)
{ok, Replies ++ Outgoing, NChannel1} ),
{ok, Replies1 ++ Replies2, NChannel1}
end. end.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
@ -1240,7 +1251,6 @@ maybe_resume_session(#channel{session = Session,
%% Deliver publish: broker -> client %% Deliver publish: broker -> client
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% return list(emqx_types:packet())
do_deliver({pubrel, MsgId}, Channel) -> do_deliver({pubrel, MsgId}, Channel) ->
{[?SN_PUBREC_MSG(?SN_PUBREL, MsgId)], Channel}; {[?SN_PUBREC_MSG(?SN_PUBREL, MsgId)], Channel};
@ -1271,6 +1281,18 @@ do_deliver(Publishes, Channel) when is_list(Publishes) ->
end, {[], Channel}, Publishes), end, {[], Channel}, Publishes),
{lists:reverse(Packets), NChannel}. {lists:reverse(Packets), NChannel}.
outgoing_deliver_and_register({Packets, Channel}) ->
{NPackets, NRegisters} =
lists:foldl(fun(P, {Acc0, Acc1}) ->
case P of
{register, _} ->
{Acc0, [P|Acc1]};
_ ->
{[P|Acc0], Acc1}
end
end, {[], []}, Packets),
{[{outgoing, lists:reverse(NPackets)}] ++ lists:reverse(NRegisters), Channel}.
message_to_packet(MsgId, Message, message_to_packet(MsgId, Message,
#channel{registry = Registry, #channel{registry = Registry,
clientinfo = #{clientid := ClientId}}) -> clientinfo = #{clientid := ClientId}}) ->
@ -1281,17 +1303,19 @@ message_to_packet(MsgId, Message,
?QOS_0 -> 0; ?QOS_0 -> 0;
_ -> MsgId _ -> MsgId
end, end,
{TopicIdType, NTopicId} = case emqx_sn_registry:lookup_topic_id(Registry, ClientId, Topic) of
case emqx_sn_registry:lookup_topic_id(Registry, ClientId, Topic) of {predef, PredefTopicId} ->
{predef, PredefTopicId} -> Flags = #mqtt_sn_flags{qos = QoS, topic_id_type = ?SN_PREDEFINED_TOPIC},
{?SN_PREDEFINED_TOPIC, PredefTopicId}; ?SN_PUBLISH_MSG(Flags, PredefTopicId, NMsgId, Payload);
TopicId when is_integer(TopicId) -> TopicId when is_integer(TopicId) ->
{?SN_NORMAL_TOPIC, TopicId}; Flags = #mqtt_sn_flags{qos = QoS, topic_id_type = ?SN_NORMAL_TOPIC},
undefined -> ?SN_PUBLISH_MSG(Flags, TopicId, NMsgId, Payload);
{?SN_SHORT_TOPIC, Topic} undefined when byte_size(Topic) =:= 2 ->
end, Flags = #mqtt_sn_flags{qos = QoS, topic_id_type = ?SN_SHORT_TOPIC},
Flags = #mqtt_sn_flags{qos = QoS, topic_id_type = TopicIdType}, ?SN_PUBLISH_MSG(Flags, Topic, NMsgId, Payload);
?SN_PUBLISH_MSG(Flags, NTopicId, NMsgId, Payload). undefined ->
{register, Topic}
end.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% Handle call %% Handle call
@ -1423,6 +1447,34 @@ handle_info(clean_authz_cache, Channel) ->
handle_info({subscribe, _}, Channel) -> handle_info({subscribe, _}, Channel) ->
{ok, Channel}; {ok, Channel};
handle_info({register, TopicName},
Channel = #channel{
registry = Registry,
session = Session}) ->
ClientId = clientid(Channel),
case emqx_sn_registry:lookup_topic_id(Registry, ClientId, TopicName) of
undefined ->
case emqx_sn_registry:register_topic(Registry, ClientId, TopicName) of
{error, Reason} ->
?SLOG(error, #{ msg => "register_topic_failed"
, topic_name => TopicName
, reason => Reason
}),
{ok, Channel};
TopicId ->
{MsgId, NSession} = emqx_session:obtain_next_pkt_id(Session),
handle_out(
register,
{TopicId, MsgId, TopicName},
Channel#channel{session = NSession})
end;
Registered ->
?SLOG(debug, #{ msg => "ignore_register_request"
, registered_as => Registered
}),
{ok, Channel}
end;
handle_info(Info, Channel) -> handle_info(Info, Channel) ->
?SLOG(error, #{ msg => "unexpected_info" ?SLOG(error, #{ msg => "unexpected_info"
, info => Info , info => Info
@ -1678,6 +1730,9 @@ interval(await_timer, #channel{session = Session}) ->
%% Helper functions %% Helper functions
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
clientid(#channel{clientinfo = #{clientid := ClientId}}) ->
ClientId.
run_hooks(Ctx, Name, Args) -> run_hooks(Ctx, Name, Args) ->
emqx_gateway_ctx:metrics_inc(Ctx, Name), emqx_gateway_ctx:metrics_inc(Ctx, Name),
emqx_hooks:run(Name, Args). emqx_hooks:run(Name, Args).

View File

@ -349,7 +349,7 @@ format(?SN_SUBACK_MSG(Flags, TopicId, MsgId, ReturnCode)) ->
[QoS, MsgId, TopicId, ReturnCode]); [QoS, MsgId, TopicId, ReturnCode]);
format(?SN_UNSUBSCRIBE_MSG(Flags, Msgid, Topic)) -> format(?SN_UNSUBSCRIBE_MSG(Flags, Msgid, Topic)) ->
#mqtt_sn_flags{topic_id_type = TopicIdType} = Flags, #mqtt_sn_flags{topic_id_type = TopicIdType} = Flags,
io_lib:format("SN_UNSUBSCRIBE(TopicIdType=~s, MsgId=~w, TopicId=~w)", io_lib:format("SN_UNSUBSCRIBE(TopicIdType=~w, MsgId=~w, TopicId=~w)",
[TopicIdType, Msgid, Topic]); [TopicIdType, Msgid, Topic]);
format(?SN_UNSUBACK_MSG(MsgId)) -> format(?SN_UNSUBACK_MSG(MsgId)) ->
io_lib:format("SN_UNSUBACK(MsgId=~w)", [MsgId]); io_lib:format("SN_UNSUBACK(MsgId=~w)", [MsgId]);

View File

@ -1048,11 +1048,6 @@ t_delivery_takeover_and_re_register(_) ->
_ = emqx:publish(emqx_message:make(test, ?QOS_2, <<"topic-b">>, <<"m2">>)), _ = emqx:publish(emqx_message:make(test, ?QOS_2, <<"topic-b">>, <<"m2">>)),
_ = emqx:publish(emqx_message:make(test, ?QOS_2, <<"topic-b">>, <<"m3">>)), _ = emqx:publish(emqx_message:make(test, ?QOS_2, <<"topic-b">>, <<"m3">>)),
emqx_logger:set_log_level(debug),
dbg:tracer(),dbg:p(all,call),
dbg:tp(emqx_gateway_cm,x),
%dbg:tpl(emqx_gateway_cm, request_stepdown,x),
{ok, NSocket} = gen_udp:open(0, [binary]), {ok, NSocket} = gen_udp:open(0, [binary]),
send_connect_msg(NSocket, <<"test">>, 0), send_connect_msg(NSocket, <<"test">>, 0),
?assertMatch(<<_, ?SN_CONNACK, ?SN_RC_ACCEPTED>>, ?assertMatch(<<_, ?SN_CONNACK, ?SN_RC_ACCEPTED>>,