diff --git a/src/host/iot_host.erl b/src/host/iot_host.erl index 8307d8f..1182364 100644 --- a/src/host/iot_host.erl +++ b/src/host/iot_host.erl @@ -15,6 +15,7 @@ %% 心跳包检测时间间隔, 15分钟检测一次 -define(HEARTBEAT_INTERVAL, 900 * 1000). +-define(AUTH_COMMAND_TIMEOUT, 10000). %% 状态 -define(STATE_DENIED, denied). @@ -79,9 +80,9 @@ get_status(Pid) when is_pid(Pid) -> gen_statem:call(Pid, get_status). %% 激活主机, true 表示激活; false表示关闭激活 --spec activate(Pid :: pid(), Auth :: boolean()) -> ok. +-spec activate(Pid :: pid(), Auth :: boolean()) -> ok | {error, term()}. activate(Pid, Auth) when is_pid(Pid), is_boolean(Auth) -> - gen_statem:call(Pid, {activate, Auth}). + gen_statem:call(Pid, {activate, Auth}, ?AUTH_COMMAND_TIMEOUT + 1000). -spec get_metric(Pid :: pid()) -> {ok, MetricInfo :: map()}. get_metric(Pid) when is_pid(Pid) -> @@ -248,23 +249,28 @@ handle_event({call, From}, {activate, true}, _, State = #state{uuid = UUID, chan case is_pid(ChannelPid) of true -> logger:debug("[iot_host] uuid: ~p, activate: true", [UUID]), - ssl_channel:activate(ChannelPid, true); + start_auth_command(ChannelPid, true, From); false -> logger:debug("[iot_host] uuid: ~p, activate: true, no channel", [UUID]) end, - {next_state, ?STATE_ACTIVATED, State, [{reply, From, ok}]}; + case is_pid(ChannelPid) of + true -> + {next_state, ?STATE_ACTIVATED, State}; + false -> + {next_state, ?STATE_ACTIVATED, State, [{reply, From, ok}]} + end; %% 关闭授权 handle_event({call, From}, {activate, false}, _, State = #state{uuid = UUID, channel_pid = ChannelPid}) -> case is_pid(ChannelPid) of true -> - ssl_channel:activate(ChannelPid, false), logger:debug("[iot_host] uuid: ~p, activate: false", [UUID]), - ssl_channel:stop(ChannelPid, closed); + start_auth_command(ChannelPid, false, From), + {keep_state, State}; false -> - logger:debug("[iot_host] uuid: ~p, activate: false, no channel", [UUID]) - end, - {next_state, ?STATE_DENIED, State#state{channel_pid = undefined, has_session = false}, [{reply, From, ok}]}; + logger:debug("[iot_host] uuid: ~p, activate: false, no channel", [UUID]), + {next_state, ?STATE_DENIED, State#state{channel_pid = undefined, has_session = false}, [{reply, From, ok}]} + end; %% 绑定channel handle_event({call, From}, {attach_channel, ChannelPid}, StateName, State = #state{uuid = UUID, channel_pid = OldChannelPid}) -> @@ -304,6 +310,16 @@ handle_event(cast, {handle, {ping, Metrics}}, ?STATE_ACTIVATED, State = #state{u handle_event(cast, heartbeat, _, State = #state{heartbeat_counter = HeartbeatCounter}) -> {keep_state, State#state{heartbeat_counter = HeartbeatCounter + 1}}; +handle_event(info, {auth_command_result, From, true, _ChannelPid, ok}, _, State) -> + {keep_state, State, [{reply, From, ok}]}; +handle_event(info, {auth_command_result, From, true, _ChannelPid, {error, Reason}}, _, State) -> + {keep_state, State, [{reply, From, {error, Reason}}]}; +handle_event(info, {auth_command_result, From, false, ChannelPid, ok}, _, State) -> + ssl_channel:stop(ChannelPid, closed), + {next_state, ?STATE_DENIED, State#state{channel_pid = undefined, has_session = false}, [{reply, From, ok}]}; +handle_event(info, {auth_command_result, From, false, _ChannelPid, {error, Reason}}, _, State) -> + {keep_state, State, [{reply, From, {error, Reason}}]}; + %% 没有收到心跳包,主机下线, 设备状态不变 handle_event(info, {timeout, _, heartbeat_ticker}, _, State = #state{uuid = UUID, heartbeat_counter = 0, channel_pid = ChannelPid}) -> logger:warning("[iot_host] uuid: ~p, heartbeat lost, devices will unknown", [UUID]), @@ -356,6 +372,15 @@ code_change(_OldVsn, StateName, State = #state{}, _Extra) -> container_call(Pid, Request) when is_pid(Pid) -> gen_statem:call(Pid, {container_call, self(), Request}). +-spec start_auth_command(pid(), boolean(), term()) -> pid(). +start_auth_command(ChannelPid, Auth, From) when is_pid(ChannelPid), is_boolean(Auth) -> + HostPid = self(), + spawn(fun() -> + Ref = ssl_channel:activate(ChannelPid, Auth), + Result = iot_host:await_reply(HostPid, Ref, ?AUTH_COMMAND_TIMEOUT), + HostPid ! {auth_command_result, From, Auth, ChannelPid, Result} + end). + -spec get_route_key(binary()) -> binary(). get_route_key(<<"">>) -> <<"/">>; diff --git a/src/transport/http/host_handler.erl b/src/transport/http/host_handler.erl index fbafa62..9c5e5ac 100644 --- a/src/transport/http/host_handler.erl +++ b/src/transport/http/host_handler.erl @@ -60,9 +60,7 @@ handle_request("POST", "/host/activate", _, #{<<"uuid">> := UUID, <<"auth">> := {ok, 200, iot_util:json_error(400, <<"host not found">>)}; {ok, Pid} when is_pid(Pid) -> logger:debug("[host_handler] activate host_id: ~p, start", [UUID]), - ok = iot_host:activate(Pid, true), - - {ok, 200, iot_util:json_data(<<"success">>)} + auth_response(iot_host:activate(Pid, true)) end; %% 处理主机的授权的 取消激活 @@ -73,9 +71,7 @@ handle_request("POST", "/host/activate", _, #{<<"uuid">> := UUID, <<"auth">> := {ok, 200, iot_util:json_error(400, <<"host not found">>)}; {ok, Pid} when is_pid(Pid) -> logger:debug("[host_handler] activate host_id: ~p, start", [UUID]), - ok = iot_host:activate(Pid, false), - - {ok, 200, iot_util:json_data(<<"success">>)} + auth_response(iot_host:activate(Pid, false)) end; %% 主机事件发布 @@ -98,4 +94,31 @@ handle_request(_, Path, _, _) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% helper methods -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \ No newline at end of file +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +auth_response(ok) -> + {ok, 200, iot_util:json_data(<<"success">>)}; +auth_response({error, Reason}) -> + {ok, auth_error_status(Reason), iot_util:json_error(auth_error_status(Reason), reason_to_binary(Reason))}. + +auth_error_status(timeout) -> + 504; +auth_error_status(<<"timeout">>) -> + 504; +auth_error_status(_) -> + 400. + +reason_to_binary(Reason) when is_binary(Reason) -> + Reason; +reason_to_binary(timeout) -> + <<"timeout">>; +reason_to_binary(invalid_response) -> + <<"invalid response">>; +reason_to_binary({denied, Reason}) -> + reason_to_binary(Reason); +reason_to_binary({failed, Reason}) -> + reason_to_binary(Reason); +reason_to_binary({channel_closed, Reason}) -> + <<"channel closed: ", (reason_to_binary(Reason))/binary>>; +reason_to_binary(Reason) -> + unicode:characters_to_binary(io_lib:format("~p", [Reason])).