refactor protocol

This commit is contained in:
Feng Lee 2015-10-30 18:00:23 +08:00
parent 1ef861715e
commit 19930b6382
2 changed files with 95 additions and 124 deletions

View File

@ -120,11 +120,11 @@ handle_call(session, _From, State = #client_state{proto_state = ProtoState}) ->
handle_call(info, _From, State = #client_state{connection = Connection, handle_call(info, _From, State = #client_state{connection = Connection,
proto_state = ProtoState}) -> proto_state = ProtoState}) ->
ClientInfo = [{Key, Val} || {Key, Val} <- ?record_to_proplist(client_state, State), lists:member(Key, ?INFO_KEYS)], ClientInfo = ?record_to_proplist(client_state, State, ?INFO_KEYS),
ProtoInfo = emqttd_protocol:info(ProtoState), ProtoInfo = emqttd_protocol:info(ProtoState),
{ok, SockStats} = Connection:getstat(?SOCK_STATS), {ok, SockStats} = Connection:getstat(?SOCK_STATS),
Info = lists:append([ClientInfo, [{proto_info, ProtoInfo}, {sock_stats, SockStats}]]), {noreply, lists:append([ClientInfo, [{proto_info, ProtoInfo},
{reply, Info, State}; {sock_stats, SockStats}]]), State};
handle_call(kick, _From, State) -> handle_call(kick, _From, State) ->
{stop, {shutdown, kick}, ok, State}; {stop, {shutdown, kick}, ok, State};
@ -176,7 +176,7 @@ handle_info(activate_sock, State) ->
handle_info({inet_async, _Sock, _Ref, {ok, Data}}, State) -> handle_info({inet_async, _Sock, _Ref, {ok, Data}}, State) ->
Size = size(Data), Size = size(Data),
?LOG(debug, "RECV: ~p", [Data], State), ?LOG(debug, "RECV <- ~p", [Data], State),
emqttd_metrics:inc('bytes/received', Size), emqttd_metrics:inc('bytes/received', Size),
received(Data, rate_limit(Size, State#client_state{await_recv = false})); received(Data, rate_limit(Size, State#client_state{await_recv = false}));
@ -203,7 +203,7 @@ handle_info({keepalive, start, Interval}, State = #client_state{connection = Con
handle_info({keepalive, check}, State = #client_state{keepalive = KeepAlive}) -> handle_info({keepalive, check}, State = #client_state{keepalive = KeepAlive}) ->
case emqttd_keepalive:check(KeepAlive) of case emqttd_keepalive:check(KeepAlive) of
{ok, KeepAlive1} -> {ok, KeepAlive1} ->
noreply(State#state{keepalive = KeepAlive1}); noreply(State#client_state{keepalive = KeepAlive1});
{error, timeout} -> {error, timeout} ->
?LOG(debug, "Keepalive timeout", [], State), ?LOG(debug, "Keepalive timeout", [], State),
shutdown(keepalive_timeout, State); shutdown(keepalive_timeout, State);

View File

@ -24,7 +24,6 @@
%%% %%%
%%% @end %%% @end
%%%----------------------------------------------------------------------------- %%%-----------------------------------------------------------------------------
-module(emqttd_protocol). -module(emqttd_protocol).
-author("Feng Lee <feng@emqtt.io>"). -author("Feng Lee <feng@emqtt.io>").
@ -33,6 +32,8 @@
-include("emqttd_protocol.hrl"). -include("emqttd_protocol.hrl").
-include("emqttd_internal.hrl").
%% API %% API
-export([init/3, info/1, clientid/1, client/1, session/1]). -export([init/3, info/1, clientid/1, client/1, session/1]).
@ -41,29 +42,26 @@
-export([process/2]). -export([process/2]).
%% Protocol State %% Protocol State
-record(proto_state, {peername, -record(proto_state, {peername, sendfun, connected = false,
sendfun, client_id, client_pid, clean_sess,
connected = false, %received CONNECT action? proto_ver, proto_name, username,
proto_ver, will_msg, keepalive, max_clientid_len = ?MAX_CLIENTID_LEN,
proto_name, session, ws_initial_headers, %% Headers from first HTTP request for websocket client
username,
client_id,
clean_sess,
session,
will_msg,
keepalive,
max_clientid_len = ?MAX_CLIENTID_LEN,
client_pid,
ws_initial_headers, %% Headers from first HTTP request for websocket client
connected_at}). connected_at}).
-type proto_state() :: #proto_state{}. -type proto_state() :: #proto_state{}.
-define(INFO_KEYS, [client_id, username, clean_sess, proto_ver, proto_name,
keepalive, will_msg, ws_initial_headers, connected_at]).
-define(LOG(Level, Format, Args, State),
lager:Level([{client, State#proto_state.client_id}], "Client(~s@~s): " ++ Format,
[State#proto_state.client_id, State#proto_state.peername | Args])).
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
%% @doc Init protocol %% @doc Init protocol
%% @end %% @end
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
init(Peername, SendFun, Opts) -> init(Peername, SendFun, Opts) ->
MaxLen = emqttd_opts:g(max_clientid_len, Opts, ?MAX_CLIENTID_LEN), MaxLen = emqttd_opts:g(max_clientid_len, Opts, ?MAX_CLIENTID_LEN),
WsInitialHeaders = emqttd_opts:g(ws_initial_headers, Opts), WsInitialHeaders = emqttd_opts:g(ws_initial_headers, Opts),
@ -73,38 +71,20 @@ init(Peername, SendFun, Opts) ->
client_pid = self(), client_pid = self(),
ws_initial_headers = WsInitialHeaders}. ws_initial_headers = WsInitialHeaders}.
info(#proto_state{client_id = ClientId, info(ProtoState) ->
username = Username, ?record_to_proplist(proto_state, ProtoState, ?INFO_KEYS).
peername = Peername,
proto_ver = ProtoVer,
proto_name = ProtoName,
keepalive = KeepAlive,
clean_sess = CleanSess,
ws_initial_headers = WsInitialHeaders,
will_msg = WillMsg,
connected_at = ConnectedAt}) ->
[{client_id, ClientId},
{username, Username},
{peername, Peername},
{proto_ver, ProtoVer},
{proto_name, ProtoName},
{keepalive, KeepAlive},
{clean_sess, CleanSess},
{ws_initial_headers, WsInitialHeaders},
{will_msg, WillMsg},
{connected_at, ConnectedAt}].
clientid(#proto_state{client_id = ClientId}) -> clientid(#proto_state{client_id = ClientId}) ->
ClientId. ClientId.
client(#proto_state{client_id = ClientId, client(#proto_state{client_id = ClientId,
client_pid = ClientPid,
peername = Peername, peername = Peername,
username = Username, username = Username,
clean_sess = CleanSess, clean_sess = CleanSess,
proto_ver = ProtoVer, proto_ver = ProtoVer,
keepalive = Keepalive, keepalive = Keepalive,
will_msg = WillMsg, will_msg = WillMsg,
client_pid = Pid,
ws_initial_headers = WsInitialHeaders, ws_initial_headers = WsInitialHeaders,
connected_at = Time}) -> connected_at = Time}) ->
WillTopic = if WillTopic = if
@ -112,7 +92,7 @@ client(#proto_state{client_id = ClientId,
true -> WillMsg#mqtt_message.topic true -> WillMsg#mqtt_message.topic
end, end,
#mqtt_client{client_id = ClientId, #mqtt_client{client_id = ClientId,
client_pid = Pid, client_pid = ClientPid,
username = Username, username = Username,
peername = Peername, peername = Peername,
clean_sess = CleanSess, clean_sess = CleanSess,
@ -127,7 +107,7 @@ session(#proto_state{session = Session}) ->
%% CONNECT Client requests a connection to a Server %% CONNECT Client requests a connection to a Server
%%A Client can only send the CONNECT Packet once over a Network Connection. %% A Client can only send the CONNECT Packet once over a Network Connection.
-spec received(mqtt_packet(), proto_state()) -> {ok, proto_state()} | {error, any()}. -spec received(mqtt_packet(), proto_state()) -> {ok, proto_state()} | {error, any()}.
received(Packet = ?PACKET(?CONNECT), State = #proto_state{connected = false}) -> received(Packet = ?PACKET(?CONNECT), State = #proto_state{connected = false}) ->
process(Packet, State#proto_state{connected = true}); process(Packet, State#proto_state{connected = true});
@ -135,20 +115,20 @@ received(Packet = ?PACKET(?CONNECT), State = #proto_state{connected = false}) ->
received(?PACKET(?CONNECT), State = #proto_state{connected = true}) -> received(?PACKET(?CONNECT), State = #proto_state{connected = true}) ->
{error, protocol_bad_connect, State}; {error, protocol_bad_connect, State};
%%Received other packets when CONNECT not arrived. %% Received other packets when CONNECT not arrived.
received(_Packet, State = #proto_state{connected = false}) -> received(_Packet, State = #proto_state{connected = false}) ->
{error, protocol_not_connected, State}; {error, protocol_not_connected, State};
received(Packet = ?PACKET(_Type), State) -> received(Packet = ?PACKET(_Type), State) ->
trace(recv, Packet, State), trace(recv, Packet, State),
case validate_packet(Packet) of case validate_packet(Packet) of
ok -> ok ->
process(Packet, State); process(Packet, State);
{error, Reason} -> {error, Reason} ->
{error, Reason, State} {error, Reason, State}
end. end.
process(Packet = ?CONNECT_PACKET(Var), State0 = #proto_state{peername = Peername}) -> process(Packet = ?CONNECT_PACKET(Var), State0) ->
#mqtt_packet_connect{proto_ver = ProtoVer, #mqtt_packet_connect{proto_ver = ProtoVer,
proto_name = ProtoName, proto_name = ProtoName,
@ -190,10 +170,8 @@ process(Packet = ?CONNECT_PACKET(Var), State0 = #proto_state{peername = Peername
exit({shutdown, Error}) exit({shutdown, Error})
end; end;
{error, Reason}-> {error, Reason}->
lager:error("~s@~s: username '~s' login failed for ~s", ?LOG(error, "Username '~s' login failed for ~s", [Username, Reason], State1),
[ClientId, emqttd_net:format(Peername), Username, Reason]),
{?CONNACK_CREDENTIALS, State1} {?CONNACK_CREDENTIALS, State1}
end; end;
ReturnCode -> ReturnCode ->
{ReturnCode, State1} {ReturnCode, State1}
@ -203,19 +181,18 @@ process(Packet = ?CONNECT_PACKET(Var), State0 = #proto_state{peername = Peername
%% Send connack %% Send connack
send(?CONNACK_PACKET(ReturnCode1), State3); send(?CONNACK_PACKET(ReturnCode1), State3);
process(Packet = ?PUBLISH_PACKET(_Qos, Topic, _PacketId, _Payload), process(Packet = ?PUBLISH_PACKET(_Qos, Topic, _PacketId, _Payload), State) ->
State = #proto_state{client_id = ClientId}) ->
case check_acl(publish, Topic, State) of case check_acl(publish, Topic, State) of
allow -> allow ->
publish(Packet, State); publish(Packet, State);
deny -> deny ->
lager:error("ACL Deny: ~s cannot publish to ~s", [ClientId, Topic]) ?LOG(error, "Cannot publish to ~s for ACL Deny", [Topic], State)
end, end,
{ok, State}; {ok, State};
process(?PUBACK_PACKET(?PUBACK, PacketId), State = #proto_state{session = Session}) -> process(?PUBACK_PACKET(?PUBACK, PacketId), State = #proto_state{session = Session}) ->
emqttd_session:puback(Session, PacketId), {ok, State}; emqttd_session:puback(Session, PacketId),
{ok, State};
process(?PUBACK_PACKET(?PUBREC, PacketId), State = #proto_state{session = Session}) -> process(?PUBACK_PACKET(?PUBREC, PacketId), State = #proto_state{session = Session}) ->
emqttd_session:pubrec(Session, PacketId), emqttd_session:pubrec(Session, PacketId),
@ -228,22 +205,21 @@ process(?PUBACK_PACKET(?PUBREL, PacketId), State = #proto_state{session = Sessio
process(?PUBACK_PACKET(?PUBCOMP, PacketId), State = #proto_state{session = Session})-> process(?PUBACK_PACKET(?PUBCOMP, PacketId), State = #proto_state{session = Session})->
emqttd_session:pubcomp(Session, PacketId), {ok, State}; emqttd_session:pubcomp(Session, PacketId), {ok, State};
%% protect from empty topic list %% Protect from empty topic table
process(?SUBSCRIBE_PACKET(PacketId, []), State) -> process(?SUBSCRIBE_PACKET(PacketId, []), State) ->
send(?SUBACK_PACKET(PacketId, []), State); send(?SUBACK_PACKET(PacketId, []), State);
process(?SUBSCRIBE_PACKET(PacketId, TopicTable), process(?SUBSCRIBE_PACKET(PacketId, TopicTable), State = #proto_state{session = Session}) ->
State = #proto_state{client_id = ClientId, session = Session}) ->
AllowDenies = [check_acl(subscribe, Topic, State) || {Topic, _Qos} <- TopicTable], AllowDenies = [check_acl(subscribe, Topic, State) || {Topic, _Qos} <- TopicTable],
case lists:member(deny, AllowDenies) of case lists:member(deny, AllowDenies) of
true -> true ->
lager:error("SUBSCRIBE from '~s' Denied: ~p", [ClientId, TopicTable]), ?LOG(error, "Cannot SUBSCRIBE ~p for ACL Deny", [TopicTable], State),
send(?SUBACK_PACKET(PacketId, [16#80 || _ <- TopicTable]), State); send(?SUBACK_PACKET(PacketId, [16#80 || _ <- TopicTable]), State);
false -> false ->
emqttd_session:subscribe(Session, PacketId, TopicTable), {ok, State} emqttd_session:subscribe(Session, PacketId, TopicTable), {ok, State}
end; end;
%% protect from empty topic list %% Protect from empty topic list
process(?UNSUBSCRIBE_PACKET(PacketId, []), State) -> process(?UNSUBSCRIBE_PACKET(PacketId, []), State) ->
send(?UNSUBACK_PACKET(PacketId), State); send(?UNSUBACK_PACKET(PacketId), State);
@ -255,72 +231,65 @@ process(?PACKET(?PINGREQ), State) ->
send(?PACKET(?PINGRESP), State); send(?PACKET(?PINGRESP), State);
process(?PACKET(?DISCONNECT), State) -> process(?PACKET(?DISCONNECT), State) ->
% clean willmsg % Clean willmsg
{stop, normal, State#proto_state{will_msg = undefined}}. {stop, normal, State#proto_state{will_msg = undefined}}.
publish(Packet = ?PUBLISH_PACKET(?QOS_0, _PacketId), publish(Packet = ?PUBLISH_PACKET(?QOS_0, _PacketId),
#proto_state{client_id = ClientId, session = Session}) -> #proto_state{client_id = ClientId, session = Session}) ->
Msg = emqttd_message:from_packet(ClientId, Packet), emqttd_session:publish(Session, emqttd_message:from_packet(ClientId, Packet));
emqttd_session:publish(Session, Msg);
publish(Packet = ?PUBLISH_PACKET(?QOS_1, PacketId), publish(Packet = ?PUBLISH_PACKET(?QOS_1, _PacketId), State) ->
State = #proto_state{client_id = ClientId, session = Session}) -> with_puback(?PUBACK, Packet, State);
publish(Packet = ?PUBLISH_PACKET(?QOS_2, _PacketId), State) ->
with_puback(?PUBREC, Packet, State).
with_puback(Type, Packet = ?PUBLISH_PACKET(_Qos, PacketId),
State = #proto_state{client_id = ClientId, session = Session}) ->
Msg = emqttd_message:from_packet(ClientId, Packet), Msg = emqttd_message:from_packet(ClientId, Packet),
case emqttd_session:publish(Session, Msg) of case emqttd_session:publish(Session, Msg) of
ok -> ok ->
send(?PUBACK_PACKET(?PUBACK, PacketId), State); send(?PUBACK_PACKET(Type, PacketId), State);
{error, Error} -> {error, Error} ->
lager:error("Client(~s): publish qos1 error - ~p", [ClientId, Error]) ?LOG(error, "PUBLISH ~p error: ~p", [PacketId, Error], State)
end;
publish(Packet = ?PUBLISH_PACKET(?QOS_2, PacketId),
State = #proto_state{client_id = ClientId, session = Session}) ->
Msg = emqttd_message:from_packet(ClientId, Packet),
case emqttd_session:publish(Session, Msg) of
ok ->
send(?PUBACK_PACKET(?PUBREC, PacketId), State);
{error, Error} ->
lager:error("Client(~s): publish qos2 error - ~p", [ClientId, Error])
end. end.
-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) when is_record(Msg, mqtt_message) -> send(Msg, State) when is_record(Msg, mqtt_message) ->
send(emqttd_message:to_packet(Msg), State); send(emqttd_message:to_packet(Msg), State);
send(Packet, State = #proto_state{sendfun = SendFun, peername = Peername}) send(Packet, State = #proto_state{sendfun = SendFun})
when is_record(Packet, mqtt_packet) -> when is_record(Packet, mqtt_packet) ->
trace(send, Packet, State), trace(send, Packet, State),
emqttd_metrics:sent(Packet), emqttd_metrics:sent(Packet),
Data = emqttd_serialiser:serialise(Packet), Data = emqttd_serialiser:serialise(Packet),
lager:debug("SENT to ~s: ~p", [emqttd_net:format(Peername), Data]), ?LOG(debug, "SENT -> ~p", [Data], State),
emqttd_metrics:inc('bytes/sent', size(Data)), emqttd_metrics:inc('bytes/sent', size(Data)),
SendFun(Data), SendFun(Data),
{ok, State}. {ok, State}.
trace(recv, Packet, #proto_state{peername = Peername, client_id = ClientId}) -> trace(recv, Packet, ProtoState) ->
lager:info([{client, ClientId}], "RECV from ~s@~s: ~s", trace2("RECV <-", Packet, ProtoState);
[ClientId, emqttd_net:format(Peername), emqttd_packet:format(Packet)]);
trace(send, Packet, #proto_state{peername = Peername, client_id = ClientId}) -> trace(send, Packet, ProtoState) ->
lager:info([{client, ClientId}], "SEND to ~s@~s: ~s", trace2("SEND ->", Packet, ProtoState).
[ClientId, emqttd_net:format(Peername), emqttd_packet:format(Packet)]).
trace2(Tag, Packet, #proto_state{peername = Peername, client_id = ClientId}) ->
lager:info([{client, ClientId}], "Client(~s@~s): ~s ~s",
[ClientId, Peername, Tag, emqttd_packet:format(Packet)]).
%% @doc redeliver PUBREL PacketId %% @doc redeliver PUBREL PacketId
redeliver({?PUBREL, PacketId}, State) -> redeliver({?PUBREL, PacketId}, State) ->
send(?PUBREL_PACKET(PacketId), State). send(?PUBREL_PACKET(PacketId), State).
shutdown(Error, #proto_state{client_id = undefined}) -> shutdown(_Error, #proto_state{client_id = undefined}) ->
lager:info("Protocol shutdown ~p", [Error]),
ignore; ignore;
shutdown(duplicate_id, #proto_state{client_id = ClientId}) -> shutdown(confict, #proto_state{client_id = ClientId}) ->
%% unregister the device
emqttd_cm:unregister(ClientId); emqttd_cm:unregister(ClientId);
%% TODO: ClientId?? shutdown(Error, State = #proto_state{client_id = ClientId, will_msg = WillMsg}) ->
shutdown(Error, #proto_state{peername = Peername, client_id = ClientId, will_msg = WillMsg}) -> ?LOG(info, "shutdown for ~p", [Error], State),
lager:info([{client, ClientId}], "Client ~s@~s: shutdown ~p",
[ClientId, emqttd_net:format(Peername), Error]),
send_willmsg(ClientId, WillMsg), send_willmsg(ClientId, WillMsg),
emqttd_broker:foreach_hooks('client.disconnected', [Error, ClientId]), emqttd_broker:foreach_hooks('client.disconnected', [Error, ClientId]),
emqttd_cm:unregister(ClientId). emqttd_cm:unregister(ClientId).
@ -341,7 +310,6 @@ maybe_set_clientid(State) ->
send_willmsg(_ClientId, undefined) -> send_willmsg(_ClientId, undefined) ->
ignore; ignore;
send_willmsg(ClientId, WillMsg) -> send_willmsg(ClientId, WillMsg) ->
lager:info("Client ~s send willmsg: ~p", [ClientId, WillMsg]),
emqttd_pubsub:publish(WillMsg#mqtt_message{from = ClientId}). emqttd_pubsub:publish(WillMsg#mqtt_message{from = ClientId}).
start_keepalive(0) -> ignore; start_keepalive(0) -> ignore;
@ -368,8 +336,9 @@ validate_connect(Connect = #mqtt_packet_connect{}, ProtoState) ->
validate_protocol(#mqtt_packet_connect{proto_ver = Ver, proto_name = Name}) -> validate_protocol(#mqtt_packet_connect{proto_ver = Ver, proto_name = Name}) ->
lists:member({Ver, Name}, ?PROTOCOL_NAMES). lists:member({Ver, Name}, ?PROTOCOL_NAMES).
validate_clientid(#mqtt_packet_connect{client_id = ClientId}, #proto_state{max_clientid_len = MaxLen}) validate_clientid(#mqtt_packet_connect{client_id = ClientId},
when ( size(ClientId) >= 1 ) andalso ( size(ClientId) =< MaxLen ) -> #proto_state{max_clientid_len = MaxLen})
when (size(ClientId) >= 1) andalso (size(ClientId) =< MaxLen) ->
true; true;
%% MQTT3.1.1 allow null clientId. %% MQTT3.1.1 allow null clientId.
@ -378,42 +347,44 @@ validate_clientid(#mqtt_packet_connect{proto_ver =?MQTT_PROTO_V311,
when size(ClientId) =:= 0 -> when size(ClientId) =:= 0 ->
true; true;
validate_clientid(#mqtt_packet_connect{proto_ver = Ver, validate_clientid(#mqtt_packet_connect{proto_ver = ProtoVer,
clean_sess = CleanSess, clean_sess = CleanSess}, ProtoState) ->
client_id = ClientId}, _ProtoState) -> ?LOG(warning, "Invalid clientId. ProtoVer: ~p, CleanSess: ~s",
lager:warning("Invalid ClientId: ~s, ProtoVer: ~p, CleanSess: ~s", [ClientId, Ver, CleanSess]), [ProtoVer, CleanSess], ProtoState),
false. false.
validate_packet(#mqtt_packet{header = #mqtt_packet_header{type = ?PUBLISH}, validate_packet(?PUBLISH_PACKET(_Qos, Topic, _PacketId, _Payload)) ->
variable = #mqtt_packet_publish{topic_name = Topic}}) ->
case emqttd_topic:validate({name, Topic}) of case emqttd_topic:validate({name, Topic}) of
true -> ok; true -> ok;
false -> lager:warning("Error publish topic: ~p", [Topic]), {error, badtopic} false -> {error, badtopic}
end; end;
validate_packet(#mqtt_packet{header = #mqtt_packet_header{type = ?SUBSCRIBE}, validate_packet(?SUBSCRIBE_PACKET(_PacketId, TopicTable)) ->
variable = #mqtt_packet_subscribe{topic_table = Topics}}) -> validate_topics(filter, TopicTable);
validate_topics(filter, Topics);
validate_packet(#mqtt_packet{header = #mqtt_packet_header{type = ?UNSUBSCRIBE},
variable = #mqtt_packet_subscribe{topic_table = Topics}}) ->
validate_packet(?UNSUBSCRIBE_PACKET(_PacketId, Topics)) ->
validate_topics(filter, Topics); validate_topics(filter, Topics);
validate_packet(_Packet) -> validate_packet(_Packet) ->
ok. ok.
validate_topics(Type, []) when Type =:= name orelse Type =:= filter -> validate_topics(_Type, []) ->
lager:error("Empty Topics!"),
{error, empty_topics}; {error, empty_topics};
validate_topics(Type, Topics) when Type =:= name orelse Type =:= filter -> validate_topics(Type, TopicTable = [{_Topic, _Qos}|_])
ErrTopics = [Topic || {Topic, Qos} <- Topics, when Type =:= name orelse Type =:= filter ->
not (emqttd_topic:validate({Type, Topic}) and validate_qos(Qos))], Valid = fun(Topic, Qos) ->
case ErrTopics of emqttd_topic:validate({Type, Topic}) and validate_qos(Qos)
end,
case [Topic || {Topic, Qos} <- TopicTable, not Valid(Topic, Qos)] of
[] -> ok; [] -> ok;
_ -> lager:error("Error Topics: ~p", [ErrTopics]), {error, badtopic} _ -> {error, badtopic}
end;
validate_topics(Type, Topics = [Topic0|_]) when is_binary(Topic0) ->
case [Topic || Topic <- Topics, not emqttd_topic:validate({Type, Topic})] of
[] -> ok;
_ -> {error, badtopic}
end. end.
validate_qos(undefined) -> validate_qos(undefined) ->
@ -423,7 +394,7 @@ validate_qos(Qos) when ?IS_QOS(Qos) ->
validate_qos(_) -> validate_qos(_) ->
false. false.
%% publish ACL is cached in process dictionary. %% PUBLISH ACL is cached in process dictionary.
check_acl(publish, Topic, State) -> check_acl(publish, Topic, State) ->
case get({acl, publish, Topic}) of case get({acl, publish, Topic}) of
undefined -> undefined ->