fix(gw): handle discard/kick event
This commit is contained in:
parent
a2823f2ad6
commit
3443aeff18
|
@ -64,7 +64,7 @@
|
||||||
%% Connection mode
|
%% Connection mode
|
||||||
connection_required :: boolean(),
|
connection_required :: boolean(),
|
||||||
%% Connection State
|
%% Connection State
|
||||||
conn_state :: idle | connected | disconnected,
|
conn_state :: conn_state(),
|
||||||
%% Session token to identity this connection
|
%% Session token to identity this connection
|
||||||
token :: binary() | undefined
|
token :: binary() | undefined
|
||||||
}).
|
}).
|
||||||
|
@ -103,8 +103,8 @@ info(Keys, Channel) when is_list(Keys) ->
|
||||||
|
|
||||||
info(conninfo, #channel{conninfo = ConnInfo}) ->
|
info(conninfo, #channel{conninfo = ConnInfo}) ->
|
||||||
ConnInfo;
|
ConnInfo;
|
||||||
info(conn_state, #channel{conn_state = CState}) ->
|
info(conn_state, #channel{conn_state = ConnState}) ->
|
||||||
CState;
|
ConnState;
|
||||||
info(clientinfo, #channel{clientinfo = ClientInfo}) ->
|
info(clientinfo, #channel{clientinfo = ClientInfo}) ->
|
||||||
ClientInfo;
|
ClientInfo;
|
||||||
info(session, #channel{session = Session}) ->
|
info(session, #channel{session = Session}) ->
|
||||||
|
@ -236,7 +236,11 @@ handle_call(subscriptions, _From, Channel) ->
|
||||||
{reply, {error, noimpl}, Channel};
|
{reply, {error, noimpl}, Channel};
|
||||||
|
|
||||||
handle_call(kick, _From, Channel) ->
|
handle_call(kick, _From, Channel) ->
|
||||||
{reply, {error, noimpl}, Channel};
|
NChannel = ensure_disconnected(kicked, Channel),
|
||||||
|
shutdown_and_reply(kicked, ok, NChannel);
|
||||||
|
|
||||||
|
handle_call(discard, _From, Channel) ->
|
||||||
|
shutdown_and_reply(discarded, ok, Channel);
|
||||||
|
|
||||||
handle_call(Req, _From, Channel) ->
|
handle_call(Req, _From, Channel) ->
|
||||||
?SLOG(error, #{msg => "unexpected_call", call => Req}),
|
?SLOG(error, #{msg => "unexpected_call", call => Req}),
|
||||||
|
@ -398,15 +402,6 @@ fix_mountpoint(_Packet, ClientInfo = #{mountpoint := Mountpoint}) ->
|
||||||
Mountpoint1 = emqx_mountpoint:replvar(Mountpoint, ClientInfo),
|
Mountpoint1 = emqx_mountpoint:replvar(Mountpoint, ClientInfo),
|
||||||
{ok, ClientInfo#{mountpoint := Mountpoint1}}.
|
{ok, ClientInfo#{mountpoint := Mountpoint1}}.
|
||||||
|
|
||||||
ensure_connected(Channel = #channel{ctx = Ctx,
|
|
||||||
conninfo = ConnInfo,
|
|
||||||
clientinfo = ClientInfo}) ->
|
|
||||||
NConnInfo = ConnInfo#{ connected_at => erlang:system_time(millisecond)
|
|
||||||
},
|
|
||||||
ok = run_hooks(Ctx, 'client.connected', [ClientInfo, NConnInfo]),
|
|
||||||
_ = run_hooks(Ctx, 'client.connack', [NConnInfo, connection_accepted, []]),
|
|
||||||
Channel#channel{conninfo = NConnInfo}.
|
|
||||||
|
|
||||||
process_connect(#channel{ctx = Ctx,
|
process_connect(#channel{ctx = Ctx,
|
||||||
session = Session,
|
session = Session,
|
||||||
conninfo = ConnInfo,
|
conninfo = ConnInfo,
|
||||||
|
@ -447,6 +442,21 @@ run_hooks(Ctx, Name, Args, Acc) ->
|
||||||
metrics_inc(Name, Ctx) ->
|
metrics_inc(Name, Ctx) ->
|
||||||
emqx_gateway_ctx:metrics_inc(Ctx, Name).
|
emqx_gateway_ctx:metrics_inc(Ctx, Name).
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Ensure connected
|
||||||
|
|
||||||
|
ensure_connected(Channel = #channel{ctx = Ctx,
|
||||||
|
conninfo = ConnInfo,
|
||||||
|
clientinfo = ClientInfo}) ->
|
||||||
|
NConnInfo = ConnInfo#{ connected_at => erlang:system_time(millisecond)
|
||||||
|
},
|
||||||
|
_ = run_hooks(Ctx, 'client.connack', [NConnInfo, connection_accepted, []]),
|
||||||
|
ok = run_hooks(Ctx, 'client.connected', [ClientInfo, NConnInfo]),
|
||||||
|
Channel#channel{conninfo = NConnInfo, conn_state = connected}.
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Ensure disconnected
|
||||||
|
|
||||||
ensure_disconnected(Reason, Channel = #channel{
|
ensure_disconnected(Reason, Channel = #channel{
|
||||||
ctx = Ctx,
|
ctx = Ctx,
|
||||||
conninfo = ConnInfo,
|
conninfo = ConnInfo,
|
||||||
|
@ -455,6 +465,12 @@ ensure_disconnected(Reason, Channel = #channel{
|
||||||
ok = run_hooks(Ctx, 'client.disconnected', [ClientInfo, Reason, NConnInfo]),
|
ok = run_hooks(Ctx, 'client.disconnected', [ClientInfo, Reason, NConnInfo]),
|
||||||
Channel#channel{conninfo = NConnInfo, conn_state = disconnected}.
|
Channel#channel{conninfo = NConnInfo, conn_state = disconnected}.
|
||||||
|
|
||||||
|
shutdown_and_reply(Reason, Reply, Channel) ->
|
||||||
|
{shutdown, Reason, Reply, Channel}.
|
||||||
|
|
||||||
|
%shutdown_and_reply(Reason, Reply, OutPkt, Channel) ->
|
||||||
|
% {shutdown, Reason, Reply, OutPkt, Channel}.
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
%% Call Chain
|
%% Call Chain
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
|
@ -87,8 +87,7 @@ paths() ->
|
||||||
, {<<"lte_lifetime">>, timestamp}
|
, {<<"lte_lifetime">>, timestamp}
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-define(query_fun, {?MODULE, query}).
|
-define(QUERY_FUN, {?MODULE, query}).
|
||||||
-define(format_fun, {?MODULE, format_channel_info}).
|
|
||||||
|
|
||||||
clients(get, #{ bindings := #{name := Name0}
|
clients(get, #{ bindings := #{name := Name0}
|
||||||
, query_string := Params
|
, query_string := Params
|
||||||
|
@ -99,14 +98,14 @@ clients(get, #{ bindings := #{name := Name0}
|
||||||
undefined ->
|
undefined ->
|
||||||
Response = emqx_mgmt_api:cluster_query(
|
Response = emqx_mgmt_api:cluster_query(
|
||||||
Params, TabName,
|
Params, TabName,
|
||||||
?CLIENT_QS_SCHEMA, ?query_fun),
|
?CLIENT_QS_SCHEMA, ?QUERY_FUN),
|
||||||
emqx_mgmt_util:generate_response(Response);
|
emqx_mgmt_util:generate_response(Response);
|
||||||
Node1 ->
|
Node1 ->
|
||||||
Node = binary_to_atom(Node1, utf8),
|
Node = binary_to_atom(Node1, utf8),
|
||||||
ParamsWithoutNode = maps:without([<<"node">>], Params),
|
ParamsWithoutNode = maps:without([<<"node">>], Params),
|
||||||
Response = emqx_mgmt_api:node_query(
|
Response = emqx_mgmt_api:node_query(
|
||||||
Node, ParamsWithoutNode,
|
Node, ParamsWithoutNode,
|
||||||
TabName, ?CLIENT_QS_SCHEMA, ?query_fun),
|
TabName, ?CLIENT_QS_SCHEMA, ?QUERY_FUN),
|
||||||
emqx_mgmt_util:generate_response(Response)
|
emqx_mgmt_util:generate_response(Response)
|
||||||
end
|
end
|
||||||
end).
|
end).
|
||||||
|
@ -456,8 +455,7 @@ schema("/gateway/:name/clients/:clientid/subscriptions") ->
|
||||||
, post =>
|
, post =>
|
||||||
#{ description => <<"Create a subscription membership">>
|
#{ description => <<"Create a subscription membership">>
|
||||||
, parameters => params_client_insta()
|
, parameters => params_client_insta()
|
||||||
%% FIXME:
|
, 'requestBody' => emqx_dashboard_swagger:schema_with_examples(
|
||||||
, requestBody => emqx_dashboard_swagger:schema_with_examples(
|
|
||||||
ref(subscription),
|
ref(subscription),
|
||||||
examples_subsctiption())
|
examples_subsctiption())
|
||||||
, responses =>
|
, responses =>
|
||||||
|
|
|
@ -51,11 +51,26 @@
|
||||||
clientinfo :: emqx_types:clientinfo(),
|
clientinfo :: emqx_types:clientinfo(),
|
||||||
%% Session
|
%% Session
|
||||||
session :: emqx_lwm2m_session:session() | undefined,
|
session :: emqx_lwm2m_session:session() | undefined,
|
||||||
|
%% Channl State
|
||||||
|
%% TODO: is there need
|
||||||
|
conn_state :: conn_state(),
|
||||||
%% Timer
|
%% Timer
|
||||||
timers :: #{atom() => disable | undefined | reference()},
|
timers :: #{atom() => disable | undefined | reference()},
|
||||||
|
%% FIXME: don't store anonymouse func
|
||||||
with_context :: function()
|
with_context :: function()
|
||||||
}).
|
}).
|
||||||
|
|
||||||
|
-type channel() :: #channel{}.
|
||||||
|
|
||||||
|
-type conn_state() :: idle | connecting | connected | disconnected.
|
||||||
|
|
||||||
|
-type reply() :: {outgoing, coap_message()}
|
||||||
|
| {outgoing, [coap_message()]}
|
||||||
|
| {event, conn_state()|updated}
|
||||||
|
| {close, Reason :: atom()}.
|
||||||
|
|
||||||
|
-type replies() :: reply() | [reply()].
|
||||||
|
|
||||||
%% TODO:
|
%% TODO:
|
||||||
-define(DEFAULT_OVERRIDE,
|
-define(DEFAULT_OVERRIDE,
|
||||||
#{ clientid => <<"">> %% Generate clientid by default
|
#{ clientid => <<"">> %% Generate clientid by default
|
||||||
|
@ -79,8 +94,8 @@ info(Keys, Channel) when is_list(Keys) ->
|
||||||
|
|
||||||
info(conninfo, #channel{conninfo = ConnInfo}) ->
|
info(conninfo, #channel{conninfo = ConnInfo}) ->
|
||||||
ConnInfo;
|
ConnInfo;
|
||||||
info(conn_state, _) ->
|
info(conn_state, #channel{conn_state = ConnState}) ->
|
||||||
connected;
|
ConnState;
|
||||||
info(clientinfo, #channel{clientinfo = ClientInfo}) ->
|
info(clientinfo, #channel{clientinfo = ClientInfo}) ->
|
||||||
ClientInfo;
|
ClientInfo;
|
||||||
info(session, #channel{session = Session}) ->
|
info(session, #channel{session = Session}) ->
|
||||||
|
@ -125,7 +140,7 @@ init(ConnInfoT = #{peername := {PeerHost, _},
|
||||||
, clientinfo = ClientInfo
|
, clientinfo = ClientInfo
|
||||||
, timers = #{}
|
, timers = #{}
|
||||||
, session = emqx_lwm2m_session:new()
|
, session = emqx_lwm2m_session:new()
|
||||||
%% FIXME: don't store anonymouse func
|
, conn_state = idle
|
||||||
, with_context = with_context(Ctx, ClientInfo)
|
, with_context = with_context(Ctx, ClientInfo)
|
||||||
}.
|
}.
|
||||||
|
|
||||||
|
@ -143,9 +158,15 @@ send_cmd(Channel, Cmd) ->
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
%% Handle incoming packet
|
%% Handle incoming packet
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
handle_in(Msg, ChannleT) ->
|
|
||||||
Channel = update_life_timer(ChannleT),
|
-spec handle_in(coap_message() | {frame_error, any()}, channel())
|
||||||
call_session(handle_coap_in, Msg, Channel).
|
-> {ok, channel()}
|
||||||
|
| {ok, replies(), channel()}
|
||||||
|
| {shutdown, Reason :: term(), channel()}
|
||||||
|
| {shutdown, Reason :: term(), replies(), channel()}.
|
||||||
|
handle_in(Msg, Channle) ->
|
||||||
|
NChannel = update_life_timer(Channle),
|
||||||
|
call_session(handle_coap_in, Msg, NChannel).
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
%% Handle Delivers from broker to client
|
%% Handle Delivers from broker to client
|
||||||
|
@ -193,10 +214,23 @@ handle_call(subscriptions, _From, Channel) ->
|
||||||
{reply, {error, noimpl}, Channel};
|
{reply, {error, noimpl}, Channel};
|
||||||
|
|
||||||
handle_call(kick, _From, Channel) ->
|
handle_call(kick, _From, Channel) ->
|
||||||
{reply, {error, noimpl}, Channel};
|
NChannel = ensure_disconnected(kicked, Channel),
|
||||||
|
shutdown_and_reply(kicked, ok, NChannel);
|
||||||
|
|
||||||
handle_call(discard, _From, Channel) ->
|
handle_call(discard, _From, Channel) ->
|
||||||
{reply, {error, noimpl}, Channel};
|
shutdown_and_reply(discarded, ok, Channel);
|
||||||
|
|
||||||
|
%% TODO: No Session Takeover
|
||||||
|
%handle_call({takeover, 'begin'}, _From, Channel = #channel{session = Session}) ->
|
||||||
|
% reply(Session, Channel#channel{takeover = true});
|
||||||
|
%
|
||||||
|
%handle_call({takeover, 'end'}, _From, Channel = #channel{session = Session,
|
||||||
|
% pendings = Pendings}) ->
|
||||||
|
% ok = emqx_session:takeover(Session),
|
||||||
|
% %% TODO: Should not drain deliver here (side effect)
|
||||||
|
% Delivers = emqx_misc:drain_deliver(),
|
||||||
|
% AllPendings = lists:append(Delivers, Pendings),
|
||||||
|
% shutdown_and_reply(takenover, AllPendings, Channel);
|
||||||
|
|
||||||
handle_call(Req, _From, Channel) ->
|
handle_call(Req, _From, Channel) ->
|
||||||
?SLOG(error, #{ msg => "unexpected_call"
|
?SLOG(error, #{ msg => "unexpected_call"
|
||||||
|
@ -239,6 +273,41 @@ terminate(Reason, #channel{ctx = Ctx,
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
%% Internal functions
|
%% Internal functions
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Ensure connected
|
||||||
|
|
||||||
|
ensure_connected(Channel = #channel{
|
||||||
|
ctx = Ctx,
|
||||||
|
conninfo = ConnInfo,
|
||||||
|
clientinfo = ClientInfo}) ->
|
||||||
|
_ = run_hooks(Ctx, 'client.connack', [ConnInfo, connection_accepted, []]),
|
||||||
|
|
||||||
|
NConnInfo = ConnInfo#{connected_at => erlang:system_time(millisecond)},
|
||||||
|
ok = run_hooks(Ctx, 'client.connected', [ClientInfo, NConnInfo]),
|
||||||
|
Channel#channel{
|
||||||
|
conninfo = NConnInfo,
|
||||||
|
conn_state = connected
|
||||||
|
}.
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Ensure disconnected
|
||||||
|
|
||||||
|
ensure_disconnected(Reason, Channel = #channel{
|
||||||
|
ctx = Ctx,
|
||||||
|
conninfo = ConnInfo,
|
||||||
|
clientinfo = ClientInfo}) ->
|
||||||
|
NConnInfo = ConnInfo#{disconnected_at => erlang:system_time(millisecond)},
|
||||||
|
ok = run_hooks(Ctx, 'client.disconnected',
|
||||||
|
[ClientInfo, Reason, NConnInfo]),
|
||||||
|
Channel#channel{conninfo = NConnInfo, conn_state = disconnected}.
|
||||||
|
|
||||||
|
shutdown_and_reply(Reason, Reply, Channel) ->
|
||||||
|
{shutdown, Reason, Reply, Channel}.
|
||||||
|
|
||||||
|
%shutdown_and_reply(Reason, Reply, OutPkt, Channel) ->
|
||||||
|
% {shutdown, Reason, Reply, OutPkt, Channel}.
|
||||||
|
|
||||||
set_peercert_infos(NoSSL, ClientInfo)
|
set_peercert_infos(NoSSL, ClientInfo)
|
||||||
when NoSSL =:= nossl;
|
when NoSSL =:= nossl;
|
||||||
NoSSL =:= undefined ->
|
NoSSL =:= undefined ->
|
||||||
|
@ -335,6 +404,7 @@ enrich_clientinfo(#coap_message{options = Options} = Msg,
|
||||||
Query = maps:get(uri_query, Options, #{}),
|
Query = maps:get(uri_query, Options, #{}),
|
||||||
case Query of
|
case Query of
|
||||||
#{<<"ep">> := Epn, <<"lt">> := Lifetime} ->
|
#{<<"ep">> := Epn, <<"lt">> := Lifetime} ->
|
||||||
|
%% FIXME: the following keys is not belong standrad protocol
|
||||||
Username = maps:get(<<"imei">>, Query, Epn),
|
Username = maps:get(<<"imei">>, Query, Epn),
|
||||||
Password = maps:get(<<"password">>, Query, undefined),
|
Password = maps:get(<<"password">>, Query, undefined),
|
||||||
ClientId = maps:get(<<"device_id">>, Query, Epn),
|
ClientId = maps:get(<<"device_id">>, Query, Epn),
|
||||||
|
@ -379,13 +449,6 @@ fix_mountpoint(_Packet, ClientInfo = #{mountpoint := Mountpoint}) ->
|
||||||
Mountpoint1 = emqx_mountpoint:replvar(Mountpoint, ClientInfo),
|
Mountpoint1 = emqx_mountpoint:replvar(Mountpoint, ClientInfo),
|
||||||
{ok, ClientInfo#{mountpoint := Mountpoint1}}.
|
{ok, ClientInfo#{mountpoint := Mountpoint1}}.
|
||||||
|
|
||||||
ensure_connected(Channel = #channel{ctx = Ctx,
|
|
||||||
conninfo = ConnInfo,
|
|
||||||
clientinfo = ClientInfo}) ->
|
|
||||||
_ = run_hooks(Ctx, 'client.connack', [ConnInfo, connection_accepted, []]),
|
|
||||||
ok = run_hooks(Ctx, 'client.connected', [ClientInfo, ConnInfo]),
|
|
||||||
Channel.
|
|
||||||
|
|
||||||
process_connect(Channel = #channel{ctx = Ctx,
|
process_connect(Channel = #channel{ctx = Ctx,
|
||||||
session = Session,
|
session = Session,
|
||||||
conninfo = ConnInfo,
|
conninfo = ConnInfo,
|
||||||
|
|
|
@ -61,7 +61,7 @@
|
||||||
session :: undefined | map(),
|
session :: undefined | map(),
|
||||||
%% ClientInfo override specs
|
%% ClientInfo override specs
|
||||||
clientinfo_override :: map(),
|
clientinfo_override :: map(),
|
||||||
%% Connection Channel
|
%% Channel State
|
||||||
conn_state :: conn_state(),
|
conn_state :: conn_state(),
|
||||||
%% Heartbeat
|
%% Heartbeat
|
||||||
heartbeat :: emqx_stomp_heartbeat:heartbeat(),
|
heartbeat :: emqx_stomp_heartbeat:heartbeat(),
|
||||||
|
@ -294,9 +294,9 @@ ensure_connected(Channel = #channel{
|
||||||
clientinfo = ClientInfo}) ->
|
clientinfo = ClientInfo}) ->
|
||||||
NConnInfo = ConnInfo#{connected_at => erlang:system_time(millisecond)},
|
NConnInfo = ConnInfo#{connected_at => erlang:system_time(millisecond)},
|
||||||
ok = run_hooks(Ctx, 'client.connected', [ClientInfo, NConnInfo]),
|
ok = run_hooks(Ctx, 'client.connected', [ClientInfo, NConnInfo]),
|
||||||
Channel#channel{conninfo = NConnInfo,
|
Channel#channel{
|
||||||
conn_state = connected
|
conninfo = NConnInfo,
|
||||||
}.
|
conn_state = connected}.
|
||||||
|
|
||||||
process_connect(Channel = #channel{
|
process_connect(Channel = #channel{
|
||||||
ctx = Ctx,
|
ctx = Ctx,
|
||||||
|
|
Loading…
Reference in New Issue