feature(mqtt): support response information (#3533)

This commit is contained in:
tigercl 2020-06-20 15:07:37 +08:00 committed by GitHub
parent 66144629bf
commit 1b6a586948
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 70 additions and 3 deletions

View File

@ -663,6 +663,11 @@ mqtt.ignore_loop_deliver = false
## Value: true | false ## Value: true | false
mqtt.strict_mode = false mqtt.strict_mode = false
## Specify the response information returned to the client
##
## Value: String
## mqtt.response_information = example
##-------------------------------------------------------------------- ##--------------------------------------------------------------------
## Zones ## Zones
##-------------------------------------------------------------------- ##--------------------------------------------------------------------
@ -868,6 +873,11 @@ zone.external.ignore_loop_deliver = false
## Value: true | false ## Value: true | false
zone.external.strict_mode = false zone.external.strict_mode = false
## Specify the response information returned to the client
##
## Value: String
## zone.external.response_information = example
##-------------------------------------------------------------------- ##--------------------------------------------------------------------
## Internal Zone ## Internal Zone
@ -954,6 +964,11 @@ zone.internal.ignore_loop_deliver = false
## Value: true | false ## Value: true | false
zone.internal.strict_mode = false zone.internal.strict_mode = false
## Specify the response information returned to the client
##
## Value: String
## zone.internal.response_information = example
## Allow the zone's clients to bypass authentication step ## Allow the zone's clients to bypass authentication step
## ##
## Value: true | false ## Value: true | false

View File

@ -805,6 +805,11 @@ end}.
{datatype, {enum, [true, false]}} {datatype, {enum, [true, false]}}
]}. ]}.
%% @doc Specify the response information returned to the client
{mapping, "mqtt.response_information", "emqx.response_information", [
{datatype, string}
]}.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% Zones %% Zones
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
@ -1019,6 +1024,11 @@ end}.
{datatype, {enum, [true, false]}} {datatype, {enum, [true, false]}}
]}. ]}.
%% @doc Specify the response information returned to the client
{mapping, "zone.$name.response_information", "emqx.zones", [
{datatype, string}
]}.
%% @doc Whether to bypass the authentication step %% @doc Whether to bypass the authentication step
{mapping, "zone.$name.bypass_auth_plugins", "emqx.zones", [ {mapping, "zone.$name.bypass_auth_plugins", "emqx.zones", [
{default, false}, {default, false},
@ -1079,6 +1089,8 @@ end}.
end; end;
("mountpoint", Val) -> ("mountpoint", Val) ->
{mountpoint, iolist_to_binary(Val)}; {mountpoint, iolist_to_binary(Val)};
("response_information", Val) ->
{response_information, iolist_to_binary(Val)};
(Opt, Val) -> (Opt, Val) ->
{list_to_atom(Opt), Val} {list_to_atom(Opt), Val}
end, end,

View File

@ -676,6 +676,7 @@ not_nacked({deliver, _Topic, Msg}) ->
handle_out(connack, {?RC_SUCCESS, SP, Props}, Channel = #channel{conninfo = ConnInfo}) -> handle_out(connack, {?RC_SUCCESS, SP, Props}, Channel = #channel{conninfo = ConnInfo}) ->
AckProps = run_fold([fun enrich_connack_caps/2, AckProps = run_fold([fun enrich_connack_caps/2,
fun enrich_server_keepalive/2, fun enrich_server_keepalive/2,
fun enrich_response_information/2,
fun enrich_assigned_clientid/2 fun enrich_assigned_clientid/2
], Props, Channel), ], Props, Channel),
NAckProps = run_hooks('client.connack', [ConnInfo, emqx_reason_codes:name(?RC_SUCCESS)], AckProps), NAckProps = run_hooks('client.connack', [ConnInfo, emqx_reason_codes:name(?RC_SUCCESS)], AckProps),
@ -1393,6 +1394,16 @@ enrich_server_keepalive(AckProps, #channel{clientinfo = #{zone := Zone}}) ->
Keepalive -> AckProps#{'Server-Keep-Alive' => Keepalive} Keepalive -> AckProps#{'Server-Keep-Alive' => Keepalive}
end. end.
%%--------------------------------------------------------------------
%% Enrich response information
enrich_response_information(AckProps, #channel{conninfo = #{conn_props := ConnProps},
clientinfo = #{zone := Zone}}) ->
case emqx_mqtt_props:get('Request-Response-Information', ConnProps, 0) of
0 -> AckProps;
1 -> AckProps#{'Response-Information' => emqx_zone:response_information(Zone)}
end.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% Enrich Assigned ClientId %% Enrich Assigned ClientId

View File

@ -45,6 +45,7 @@
, session_expiry_interval/1 , session_expiry_interval/1
, force_gc_policy/1 , force_gc_policy/1
, force_shutdown_policy/1 , force_shutdown_policy/1
, response_information/1
, get_env/2 , get_env/2
, get_env/3 , get_env/3
]}). ]}).
@ -72,6 +73,7 @@
, session_expiry_interval/1 , session_expiry_interval/1
, force_gc_policy/1 , force_gc_policy/1
, force_shutdown_policy/1 , force_shutdown_policy/1
, response_information/1
]). ]).
-export([ init_gc_state/1 -export([ init_gc_state/1
@ -204,6 +206,10 @@ force_gc_policy(Zone) ->
force_shutdown_policy(Zone) -> force_shutdown_policy(Zone) ->
get_env(Zone, force_shutdown_policy). get_env(Zone, force_shutdown_policy).
-spec(response_information(zone()) -> string()).
response_information(Zone) ->
get_env(Zone, response_information).
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% APIs %% APIs
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------

View File

@ -58,7 +58,8 @@ end_per_suite(_Config) ->
emqx_session, emqx_session,
emqx_broker, emqx_broker,
emqx_hooks, emqx_hooks,
emqx_cm emqx_cm,
emqx_zone
]). ]).
init_per_testcase(_TestCase, Config) -> init_per_testcase(_TestCase, Config) ->
@ -394,6 +395,27 @@ t_handle_out_connack_sucess(_) ->
emqx_channel:handle_out(connack, {?RC_SUCCESS, 0, #{}}, channel()), emqx_channel:handle_out(connack, {?RC_SUCCESS, 0, #{}}, channel()),
?assertEqual(connected, emqx_channel:info(conn_state, Channel)). ?assertEqual(connected, emqx_channel:info(conn_state, Channel)).
t_handle_out_connack_response_information(_) ->
ok = meck:expect(emqx_cm, open_session,
fun(true, _ClientInfo, _ConnInfo) ->
{ok, #{session => session(), present => false}}
end),
ok = meck:expect(emqx_zone, response_information, fun(_) -> test end),
IdleChannel = channel(#{conn_state => idle}),
{ok, [{event, connected}, {connack, ?CONNACK_PACKET(?RC_SUCCESS, 0, #{'Response-Information' := test})}], _} =
emqx_channel:handle_in(?CONNECT_PACKET(connpkt(#{'Request-Response-Information' => 1})), IdleChannel).
t_handle_out_connack_not_response_information(_) ->
ok = meck:expect(emqx_cm, open_session,
fun(true, _ClientInfo, _ConnInfo) ->
{ok, #{session => session(), present => false}}
end),
ok = meck:expect(emqx_zone, response_information, fun(_) -> test end),
IdleChannel = channel(#{conn_state => idle}),
{ok, [{event, connected}, {connack, ?CONNACK_PACKET(?RC_SUCCESS, 0, AckProps)}], _} =
emqx_channel:handle_in(?CONNECT_PACKET(connpkt(#{'Request-Response-Information' => 0})), IdleChannel),
?assertEqual(false, maps:is_key('Response-Information', AckProps)).
t_handle_out_connack_failure(_) -> t_handle_out_connack_failure(_) ->
{shutdown, not_authorized, ?CONNACK_PACKET(?RC_NOT_AUTHORIZED), _Chan} = {shutdown, not_authorized, ?CONNACK_PACKET(?RC_NOT_AUTHORIZED), _Chan} =
emqx_channel:handle_out(connack, ?RC_NOT_AUTHORIZED, channel()). emqx_channel:handle_out(connack, ?RC_NOT_AUTHORIZED, channel()).
@ -639,14 +661,15 @@ clientinfo(InitProps) ->
topic_filters() -> topic_filters() ->
[{<<"+">>, ?DEFAULT_SUBOPTS}, {<<"#">>, ?DEFAULT_SUBOPTS}]. [{<<"+">>, ?DEFAULT_SUBOPTS}, {<<"#">>, ?DEFAULT_SUBOPTS}].
connpkt() -> connpkt() -> connpkt(#{}).
connpkt(Props) ->
#mqtt_packet_connect{ #mqtt_packet_connect{
proto_name = <<"MQTT">>, proto_name = <<"MQTT">>,
proto_ver = ?MQTT_PROTO_V4, proto_ver = ?MQTT_PROTO_V4,
is_bridge = false, is_bridge = false,
clean_start = true, clean_start = true,
keepalive = 30, keepalive = 30,
properties = #{}, properties = Props,
clientid = <<"clientid">>, clientid = <<"clientid">>,
username = <<"username">>, username = <<"username">>,
password = <<"passwd">> password = <<"passwd">>