refactor records

This commit is contained in:
Ery Lee 2015-04-15 12:37:44 +08:00
parent 23d774ce1a
commit 8c28bbcc7a
18 changed files with 175 additions and 150 deletions

View File

@ -128,7 +128,7 @@
-type mqtt_packet_id() :: 1..16#ffff | undefined. -type mqtt_packet_id() :: 1..16#ffff | undefined.
-record(mqtt_packet_connect, { -record(mqtt_packet_connect, {
client_id = <<>> :: binary(), clientid = <<>> :: binary(),
proto_ver = ?MQTT_PROTO_V311 :: mqtt_vsn(), proto_ver = ?MQTT_PROTO_V311 :: mqtt_vsn(),
proto_name = <<"MQTT">> :: binary(), proto_name = <<"MQTT">> :: binary(),
will_retain = false :: boolean(), will_retain = false :: boolean(),
@ -225,3 +225,4 @@
-define(PACKET(Type), -define(PACKET(Type),
#mqtt_packet{header = #mqtt_packet_header{type = Type}}). #mqtt_packet{header = #mqtt_packet_header{type = Type}}).

View File

@ -104,7 +104,7 @@ dump_variable(#mqtt_packet_connect{
will_flag = WillFlag, will_flag = WillFlag,
clean_sess = CleanSess, clean_sess = CleanSess,
keep_alive = KeepAlive, keep_alive = KeepAlive,
client_id = ClientId, clientid = ClientId,
will_topic = WillTopic, will_topic = WillTopic,
will_msg = WillMsg, will_msg = WillMsg,
username = Username, username = Username,

View File

@ -114,7 +114,7 @@ parse_frame(Bin, #mqtt_packet_header{type = Type, qos = Qos} = Header, Length)
will_flag = bool(WillFlag), will_flag = bool(WillFlag),
clean_sess = bool(CleanSession), clean_sess = bool(CleanSession),
keep_alive = KeepAlive, keep_alive = KeepAlive,
client_id = ClientId, clientid = ClientId,
will_topic = WillTopic, will_topic = WillTopic,
will_msg = WillMsg, will_msg = WillMsg,
username = UserName, username = UserName,

View File

@ -60,7 +60,7 @@ serialise_header(#mqtt_packet_header{type = Type,
VariableBin/binary, VariableBin/binary,
PayloadBin/binary>>. PayloadBin/binary>>.
serialise_variable(?CONNECT, #mqtt_packet_connect{client_id = ClientId, serialise_variable(?CONNECT, #mqtt_packet_connect{clientid = ClientId,
proto_ver = ProtoVer, proto_ver = ProtoVer,
proto_name = ProtoName, proto_name = ProtoName,
will_retain = WillRetain, will_retain = WillRetain,

View File

@ -52,7 +52,7 @@
-type mqtt_topic() :: #mqtt_topic{}. -type mqtt_topic() :: #mqtt_topic{}.
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
%% MQTT Topic Subscriber %% MQTT Subscriber
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
-record(mqtt_subscriber, { -record(mqtt_subscriber, {
topic :: binary(), topic :: binary(),
@ -99,13 +99,19 @@
retain = false :: boolean(), retain = false :: boolean(),
dup = false :: boolean(), dup = false :: boolean(),
msgid :: mqtt_msgid(), msgid :: mqtt_msgid(),
payload :: binary()}). payload :: binary()
}).
-type mqtt_message() :: #mqtt_message{}. -type mqtt_message() :: #mqtt_message{}.
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
%% MQTT Plugin %% MQTT Plugin
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
-record(mqtt_plugin, {
name,
version,
attrs,
description
}).
-record(mqtt_plugin, {name, version, attrs, description}).

View File

@ -94,29 +94,29 @@ bin(B) when is_binary(B) ->
%% %%
%% @end %% @end
%%%----------------------------------------------------------------------------- %%%-----------------------------------------------------------------------------
-spec match(mqtt_user(), topic(), rule()) -> {matched, allow} | {matched, deny} | nomatch. -spec match(mqtt_client(), topic(), rule()) -> {matched, allow} | {matched, deny} | nomatch.
match(_User, _Topic, {AllowDeny, all}) when (AllowDeny =:= allow) orelse (AllowDeny =:= deny) -> match(_Client, _Topic, {AllowDeny, all}) when (AllowDeny =:= allow) orelse (AllowDeny =:= deny) ->
{matched, AllowDeny}; {matched, AllowDeny};
match(User, Topic, {AllowDeny, Who, _PubSub, TopicFilters}) match(Client, Topic, {AllowDeny, Who, _PubSub, TopicFilters})
when (AllowDeny =:= allow) orelse (AllowDeny =:= deny) -> when (AllowDeny =:= allow) orelse (AllowDeny =:= deny) ->
case match_who(User, Who) andalso match_topics(User, Topic, TopicFilters) of case match_who(Client, Who) andalso match_topics(Client, Topic, TopicFilters) of
true -> {matched, AllowDeny}; true -> {matched, AllowDeny};
false -> nomatch false -> nomatch
end. end.
match_who(_User, all) -> match_who(_Client, all) ->
true; true;
match_who(_User, {user, all}) -> match_who(_Client, {user, all}) ->
true; true;
match_who(_User, {client, all}) -> match_who(_Client, {client, all}) ->
true; true;
match_who(#mqtt_user{clientid = ClientId}, {client, ClientId}) -> match_who(#mqtt_client{clientid = ClientId}, {client, ClientId}) ->
true; true;
match_who(#mqtt_user{username = Username}, {user, Username}) -> match_who(#mqtt_client{username = Username}, {user, Username}) ->
true; true;
match_who(#mqtt_user{ipaddr = undefined}, {ipaddr, _Tup}) -> match_who(#mqtt_client{ipaddr = undefined}, {ipaddr, _Tup}) ->
false; false;
match_who(#mqtt_user{ipaddr = IP}, {ipaddr, {_CDIR, Start, End}}) -> match_who(#mqtt_client{ipaddr = IP}, {ipaddr, {_CDIR, Start, End}}) ->
I = esockd_access:atoi(IP), I = esockd_access:atoi(IP),
I >= Start andalso I =< End; I >= Start andalso I =< End;
match_who(_User, _Who) -> match_who(_User, _Who) ->
@ -143,14 +143,15 @@ feed_var(User, Pattern) ->
feed_var(User, Pattern, []). feed_var(User, Pattern, []).
feed_var(_User, [], Acc) -> feed_var(_User, [], Acc) ->
lists:reverse(Acc); lists:reverse(Acc);
feed_var(User = #mqtt_user{clientid = undefined}, [<<"$c">>|Words], Acc) -> feed_var(Client = #mqtt_client{clientid = undefined}, [<<"$c">>|Words], Acc) ->
feed_var(User, Words, [<<"$c">>|Acc]); feed_var(Client, Words, [<<"$c">>|Acc]);
feed_var(User = #mqtt_user{clientid = ClientId}, [<<"$c">>|Words], Acc) -> feed_var(Client = #mqtt_client{clientid = ClientId}, [<<"$c">>|Words], Acc) ->
feed_var(User, Words, [ClientId |Acc]); feed_var(Client, Words, [ClientId |Acc]);
feed_var(User = #mqtt_user{username = undefined}, [<<"$u">>|Words], Acc) -> feed_var(Client = #mqtt_client{username = undefined}, [<<"$u">>|Words], Acc) ->
feed_var(User, Words, [<<"$u">>|Acc]); feed_var(Client, Words, [<<"$u">>|Acc]);
feed_var(User = #mqtt_user{username = Username}, [<<"$u">>|Words], Acc) -> feed_var(Client = #mqtt_client{username = Username}, [<<"$u">>|Words], Acc) ->
feed_var(User, Words, [Username|Acc]); feed_var(Client, Words, [Username|Acc]);
feed_var(User, [W|Words], Acc) -> feed_var(Client, [W|Words], Acc) ->
feed_var(User, Words, [W|Acc]). feed_var(Client, Words, [W|Acc]).

View File

@ -83,25 +83,25 @@ start_link(AclMods) ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [AclMods], []). gen_server:start_link({local, ?SERVER}, ?MODULE, [AclMods], []).
%% @doc Check ACL. %% @doc Check ACL.
-spec check({User, PubSub, Topic}) -> allow | deny when -spec check({Client, PubSub, Topic}) -> allow | deny when
User :: mqtt_user(), Client :: mqtt_client(),
PubSub :: pubsub(), PubSub :: pubsub(),
Topic :: binary(). Topic :: binary().
check({User, PubSub, Topic}) when PubSub =:= publish orelse PubSub =:= subscribe -> check({Client, PubSub, Topic}) when PubSub =:= publish orelse PubSub =:= subscribe ->
case ets:lookup(?ACL_TABLE, acl_modules) of case ets:lookup(?ACL_TABLE, acl_modules) of
[] -> allow; [] -> allow;
[{_, AclMods}] -> check({User, PubSub, Topic}, AclMods) [{_, AclMods}] -> check({Client, PubSub, Topic}, AclMods)
end. end.
check({#mqtt_user{clientid = ClientId}, PubSub, Topic}, []) -> check({#mqtt_client{clientid = ClientId}, PubSub, Topic}, []) ->
lager:error("ACL: nomatch when ~s ~s ~s", [ClientId, PubSub, Topic]), lager:error("ACL: nomatch when ~s ~s ~s", [ClientId, PubSub, Topic]),
allow; allow;
check({User, PubSub, Topic}, [{M, State}|AclMods]) -> check({Client, PubSub, Topic}, [{M, State}|AclMods]) ->
case M:check_acl({User, PubSub, Topic}, State) of case M:check_acl({Client, PubSub, Topic}, State) of
allow -> allow; allow -> allow;
deny -> deny; deny -> deny;
ignore -> check({User, PubSub, Topic}, AclMods) ignore -> check({Client, PubSub, Topic}, AclMods)
end. end.
%% @doc Reload ACL. %% @doc Reload ACL.

View File

@ -90,13 +90,13 @@ filter(_PubSub, {_AllowDeny, _Who, _, _Topics}) ->
false. false.
%% @doc Check ACL. %% @doc Check ACL.
-spec check_acl({User, PubSub, Topic}, State) -> allow | deny | ignore when -spec check_acl({Client, PubSub, Topic}, State) -> allow | deny | ignore when
User :: mqtt_user(), Client :: mqtt_client(),
PubSub :: pubsub(), PubSub :: pubsub(),
Topic :: binary(), Topic :: binary(),
State :: #state{}. State :: #state{}.
check_acl({User, PubSub, Topic}, #state{nomatch = Default}) -> check_acl({Client, PubSub, Topic}, #state{nomatch = Default}) ->
case match(User, Topic, lookup(PubSub)) of case match(Client, Topic, lookup(PubSub)) of
{matched, allow} -> allow; {matched, allow} -> allow;
{matched, deny} -> deny; {matched, deny} -> deny;
nomatch -> Default nomatch -> Default
@ -108,12 +108,12 @@ lookup(PubSub) ->
[{PubSub, Rules}] -> Rules [{PubSub, Rules}] -> Rules
end. end.
match(_User, _Topic, []) -> match(_Client, _Topic, []) ->
nomatch; nomatch;
match(User, Topic, [Rule|Rules]) -> match(Client, Topic, [Rule|Rules]) ->
case emqttd_access_rule:match(User, Topic, Rule) of case emqttd_access_rule:match(Client, Topic, Rule) of
nomatch -> match(User, Topic, Rules); nomatch -> match(Client, Topic, Rules);
{matched, AllowDeny} -> {matched, AllowDeny} {matched, AllowDeny} -> {matched, AllowDeny}
end. end.

View File

@ -71,15 +71,15 @@ init(Opts) ->
end, end,
{ok, Opts}. {ok, Opts}.
check(#mqtt_user{clientid = undefined}, _Password, []) -> check(#mqtt_client{clientid = undefined}, _Password, []) ->
{error, "ClientId undefined"}; {error, "ClientId undefined"};
check(#mqtt_user{clientid = ClientId, ipaddr = IpAddr}, _Password, []) -> check(#mqtt_client{clientid = ClientId, ipaddr = IpAddr}, _Password, []) ->
check_clientid_only(ClientId, IpAddr); check_clientid_only(ClientId, IpAddr);
check(#mqtt_user{clientid = ClientId, ipaddr = IpAddr}, _Password, [{password, no}|_]) -> check(#mqtt_client{clientid = ClientId, ipaddr = IpAddr}, _Password, [{password, no}|_]) ->
check_clientid_only(ClientId, IpAddr); check_clientid_only(ClientId, IpAddr);
check(_User, undefined, [{password, yes}|_]) -> check(_Client, undefined, [{password, yes}|_]) ->
{error, "Password undefined"}; {error, "Password undefined"};
check(#mqtt_user{clientid = ClientId}, Password, [{password, yes}|_]) -> check(#mqtt_client{clientid = ClientId}, Password, [{password, yes}|_]) ->
case mnesia:dirty_read(?AUTH_CLIENTID_TABLE, ClientId) of case mnesia:dirty_read(?AUTH_CLIENTID_TABLE, ClientId) of
[] -> {error, "ClientId Not Found"}; [] -> {error, "ClientId Not Found"};
[#?AUTH_CLIENTID_TABLE{password = Password}] -> ok; %% TODO: plaintext?? [#?AUTH_CLIENTID_TABLE{password = Password}] -> ok; %% TODO: plaintext??

View File

@ -67,11 +67,11 @@ init(Opts) ->
mnesia:add_table_copy(?AUTH_USERNAME_TABLE, node(), ram_copies), mnesia:add_table_copy(?AUTH_USERNAME_TABLE, node(), ram_copies),
{ok, Opts}. {ok, Opts}.
check(#mqtt_user{username = undefined}, _Password, _Opts) -> check(#mqtt_client{username = undefined}, _Password, _Opts) ->
{error, "Username undefined"}; {error, "Username undefined"};
check(_User, undefined, _Opts) -> check(_User, undefined, _Opts) ->
{error, "Password undefined"}; {error, "Password undefined"};
check(#mqtt_user{username = Username}, Password, _Opts) -> check(#mqtt_client{username = Username}, Password, _Opts) ->
case mnesia:dirty_read(?AUTH_USERNAME_TABLE, Username) of case mnesia:dirty_read(?AUTH_USERNAME_TABLE, Username) of
[] -> [] ->
{error, "Username Not Found"}; {error, "Username Not Found"};

View File

@ -32,6 +32,8 @@
-include_lib("emqtt/include/emqtt_packet.hrl"). -include_lib("emqtt/include/emqtt_packet.hrl").
-include("emqttd.hrl").
%% API Function Exports %% API Function Exports
-export([start_link/3]). -export([start_link/3]).

View File

@ -103,7 +103,7 @@ handle_info({stop, duplicate_id, _NewPid}, State=#state{proto_state = ProtoState
%% need transfer data??? %% need transfer data???
%% emqttd_client:transfer(NewPid, Data), %% emqttd_client:transfer(NewPid, Data),
lager:error("Shutdown for duplicate clientid: ~s, conn:~s", lager:error("Shutdown for duplicate clientid: ~s, conn:~s",
[emqttd_protocol:client_id(ProtoState), ConnName]), [emqttd_protocol:clientid(ProtoState), ConnName]),
stop({shutdown, duplicate_id}, State); stop({shutdown, duplicate_id}, State);
%%TODO: ok?? %%TODO: ok??
@ -255,7 +255,7 @@ inc(_) ->
notify(disconnected, _Reason, undefined) -> ingore; notify(disconnected, _Reason, undefined) -> ingore;
notify(disconnected, {shutdown, Reason}, ProtoState) -> notify(disconnected, {shutdown, Reason}, ProtoState) ->
emqttd_event:notify({disconnected, emqttd_protocol:client_id(ProtoState), [{reason, Reason}]}); emqttd_event:notify({disconnected, emqttd_protocol:clientid(ProtoState), [{reason, Reason}]});
notify(disconnected, Reason, ProtoState) -> notify(disconnected, Reason, ProtoState) ->
emqttd_event:notify({disconnected, emqttd_protocol:client_id(ProtoState), [{reason, Reason}]}). emqttd_event:notify({disconnected, emqttd_protocol:clientid(ProtoState), [{reason, Reason}]}).

View File

@ -28,6 +28,8 @@
-author('feng@emqtt.io'). -author('feng@emqtt.io').
-include_lib("emqtt/include/emqtt_packet.hrl").
-include("emqttd.hrl"). -include("emqttd.hrl").
-import(proplists, [get_value/2, get_value/3]). -import(proplists, [get_value/2, get_value/3]).

View File

@ -91,10 +91,7 @@ create_tables() ->
ok = emqttd_trie:mnesia(create), ok = emqttd_trie:mnesia(create),
ok = emqttd_pubsub:mnesia(create), ok = emqttd_pubsub:mnesia(create),
%% TODO: retained messages, this table should not be copied... %% TODO: retained messages, this table should not be copied...
ok = create_table(message_retained, [ ok = emqttd_retained:mnesia(create).
{type, ordered_set},
{ram_copies, [node()]},
{attributes, record_info(fields, message_retained)}]).
create_table(Table, Attrs) -> create_table(Table, Attrs) ->
case mnesia:create_table(Table, Attrs) of case mnesia:create_table(Table, Attrs) of
@ -111,9 +108,9 @@ create_table(Table, Attrs) ->
%% @end %% @end
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
copy_tables() -> copy_tables() ->
ok = emqttd_trie:mnesia(create), ok = emqttd_trie:mnesia(replicate),
ok = emqttd_pubsub:mnesia(create), ok = emqttd_pubsub:mnesia(replicate),
ok = copy_table(message_retained). ok = emqttd_retained:mnesia(replicate).
copy_table(Table) -> copy_table(Table) ->
case mnesia:add_table_copy(Table, node(), ram_copies) of case mnesia:add_table_copy(Table, node(), ram_copies) of

View File

@ -31,7 +31,7 @@
-include_lib("emqtt/include/emqtt_packet.hrl"). -include_lib("emqtt/include/emqtt_packet.hrl").
%% API %% API
-export([init/2, client_id/1]). -export([init/2, clientid/1]).
-export([received/2, send/2, redeliver/2, shutdown/2]). -export([received/2, send/2, redeliver/2, shutdown/2]).
@ -47,7 +47,7 @@
proto_name, proto_name,
%packet_id, %packet_id,
username, username,
client_id, clientid,
clean_sess, clean_sess,
session, %% session state or session pid session, %% session state or session pid
will_msg, will_msg,
@ -63,17 +63,20 @@ init({Transport, Socket, Peername}, Opts) ->
peername = Peername, peername = Peername,
max_clientid_len = proplists:get_value(max_clientid_len, Opts, ?MAX_CLIENTID_LEN)}. max_clientid_len = proplists:get_value(max_clientid_len, Opts, ?MAX_CLIENTID_LEN)}.
client_id(#proto_state{client_id = ClientId}) -> ClientId. clientid(#proto_state{clientid = ClientId}) -> ClientId.
client(#proto_state{peername = {Addr, _Port}, clientid = ClientId, username = Username}) ->
#mqtt_client{clientid = ClientId, username = Username, ipaddr = Addr}.
%%SHOULD be registered in emqttd_cm %%SHOULD be registered in emqttd_cm
info(#proto_state{proto_ver = ProtoVer, info(#proto_state{proto_ver = ProtoVer,
proto_name = ProtoName, proto_name = ProtoName,
client_id = ClientId, clientid = ClientId,
clean_sess = CleanSess, clean_sess = CleanSess,
will_msg = WillMsg}) -> will_msg = WillMsg}) ->
[{proto_ver, ProtoVer}, [{proto_ver, ProtoVer},
{proto_name, ProtoName}, {proto_name, ProtoName},
{client_id, ClientId}, {clientid, ClientId},
{clean_sess, CleanSess}, {clean_sess, CleanSess},
{will_msg, WillMsg}]. {will_msg, WillMsg}].
@ -92,7 +95,7 @@ received(_Packet, State = #proto_state{connected = false}) ->
{error, protocol_not_connected, State}; {error, protocol_not_connected, State};
received(Packet = ?PACKET(_Type), State = #proto_state{peername = Peername, received(Packet = ?PACKET(_Type), State = #proto_state{peername = Peername,
client_id = ClientId}) -> clientid = ClientId}) ->
lager:debug("RECV from ~s@~s: ~s", [ClientId, emqttd_net:format(Peername), emqtt_packet:dump(Packet)]), lager:debug("RECV from ~s@~s: ~s", [ClientId, emqttd_net:format(Peername), emqtt_packet:dump(Packet)]),
case validate_packet(Packet) of case validate_packet(Packet) of
ok -> ok ->
@ -108,24 +111,24 @@ handle(Packet = ?CONNECT_PACKET(Var), State = #proto_state{peername = Peername =
password = Password, password = Password,
clean_sess = CleanSess, clean_sess = CleanSess,
keep_alive = KeepAlive, keep_alive = KeepAlive,
client_id = ClientId} = Var, clientid = ClientId} = Var,
lager:debug("RECV from ~s@~s: ~s", [ClientId, emqttd_net:format(Peername), emqtt_packet:dump(Packet)]), lager:debug("RECV from ~s@~s: ~s", [ClientId, emqttd_net:format(Peername), emqtt_packet:dump(Packet)]),
State1 = State#proto_state{proto_ver = ProtoVer, State1 = State#proto_state{proto_ver = ProtoVer,
username = Username, username = Username,
client_id = ClientId, clientid = ClientId,
clean_sess = CleanSess}, clean_sess = CleanSess},
{ReturnCode1, State2} = {ReturnCode1, State2} =
case validate_connect(Var, State) of case validate_connect(Var, State) of
?CONNACK_ACCEPT -> ?CONNACK_ACCEPT ->
User = #mqtt_user{username = Username, ipaddr = Addr, clientid = ClientId}, Client = #mqtt_client{clientid = ClientId, username = Username, ipaddr = Addr},
case emqttd_auth:login(User, Password) of case emqttd_auth:login(Client, Password) of
ok -> ok ->
ClientId1 = clientid(ClientId, State), ClientId1 = clientid(ClientId, State),
start_keepalive(KeepAlive), start_keepalive(KeepAlive),
emqttd_cm:register(ClientId1), emqttd_cm:register(ClientId1),
{?CONNACK_ACCEPT, State1#proto_state{client_id = ClientId1, {?CONNACK_ACCEPT, State1#proto_state{clientid = ClientId1,
will_msg = willmsg(Var)}}; will_msg = willmsg(Var)}};
{error, Reason}-> {error, Reason}->
lager:error("~s@~s: username '~s' login failed - ~s", [ClientId, emqttd_net:format(Peername), Username, Reason]), lager:error("~s@~s: username '~s' login failed - ~s", [ClientId, emqttd_net:format(Peername), Username, Reason]),
@ -142,8 +145,8 @@ handle(Packet = ?CONNECT_PACKET(Var), State = #proto_state{peername = Peername =
{ok, State2#proto_state{session = Session}}; {ok, State2#proto_state{session = Session}};
handle(Packet = ?PUBLISH_PACKET(?QOS_0, Topic, _PacketId, _Payload), handle(Packet = ?PUBLISH_PACKET(?QOS_0, Topic, _PacketId, _Payload),
State = #proto_state{client_id = ClientId, session = Session}) -> State = #proto_state{clientid = ClientId, session = Session}) ->
case emqttd_acl:check({mqtt_user(State), publish, Topic}) of case emqttd_acl:check({client(State), publish, Topic}) of
allow -> allow ->
emqttd_session:publish(Session, {?QOS_0, emqttd_message:from_packet(Packet)}); emqttd_session:publish(Session, {?QOS_0, emqttd_message:from_packet(Packet)});
deny -> deny ->
@ -152,8 +155,8 @@ handle(Packet = ?PUBLISH_PACKET(?QOS_0, Topic, _PacketId, _Payload),
{ok, State}; {ok, State};
handle(Packet = ?PUBLISH_PACKET(?QOS_1, Topic, PacketId, _Payload), handle(Packet = ?PUBLISH_PACKET(?QOS_1, Topic, PacketId, _Payload),
State = #proto_state{client_id = ClientId, session = Session}) -> State = #proto_state{clientid = ClientId, session = Session}) ->
case emqttd_acl:check({mqtt_user(State), publish, Topic}) of case emqttd_acl:check({client(State), publish, Topic}) of
allow -> allow ->
emqttd_session:publish(Session, {?QOS_1, emqttd_message:from_packet(Packet)}), emqttd_session:publish(Session, {?QOS_1, emqttd_message:from_packet(Packet)}),
send(?PUBACK_PACKET(?PUBACK, PacketId), State); send(?PUBACK_PACKET(?PUBACK, PacketId), State);
@ -163,8 +166,8 @@ handle(Packet = ?PUBLISH_PACKET(?QOS_1, Topic, PacketId, _Payload),
end; end;
handle(Packet = ?PUBLISH_PACKET(?QOS_2, Topic, PacketId, _Payload), handle(Packet = ?PUBLISH_PACKET(?QOS_2, Topic, PacketId, _Payload),
State = #proto_state{client_id = ClientId, session = Session}) -> State = #proto_state{clientid = ClientId, session = Session}) ->
case emqttd_acl:check({mqtt_user(State), publish, Topic}) of case emqttd_acl:check({client(State), publish, Topic}) of
allow -> allow ->
NewSession = emqttd_session:publish(Session, {?QOS_2, emqttd_message:from_packet(Packet)}), NewSession = emqttd_session:publish(Session, {?QOS_2, emqttd_message:from_packet(Packet)}),
send(?PUBACK_PACKET(?PUBREC, PacketId), State#proto_state{session = NewSession}); send(?PUBACK_PACKET(?PUBREC, PacketId), State#proto_state{session = NewSession});
@ -187,11 +190,12 @@ handle(?PUBACK_PACKET(Type, PacketId), State = #proto_state{session = Session})
end, end,
{ok, NewState}; {ok, NewState};
handle(?SUBSCRIBE_PACKET(PacketId, TopicTable), State = #proto_state{session = Session}) -> handle(?SUBSCRIBE_PACKET(PacketId, TopicTable), State = #proto_state{clientid = ClientId, session = Session}) ->
AllowDenies = [emqttd_acl:check({mqtt_user(State), subscribe, Topic}) || {Topic, _Qos} <- TopicTable], AllowDenies = [emqttd_acl:check({client(State), subscribe, Topic}) || {Topic, _Qos} <- TopicTable],
case lists:member(deny, AllowDenies) of case lists:member(deny, AllowDenies) of
true -> true ->
%%TODO: return 128 QoS when deny... %%TODO: return 128 QoS when deny...
lager:error("SUBSCRIBE from '~s' Denied: ~p", [ClientId, TopicTable]),
{ok, State}; {ok, State};
false -> false ->
{ok, NewSession, GrantedQos} = emqttd_session:subscribe(Session, TopicTable), {ok, NewSession, GrantedQos} = emqttd_session:subscribe(Session, TopicTable),
@ -225,7 +229,7 @@ send({_From, Message = #mqtt_message{qos = Qos}}, State = #proto_state{session =
{Message1, NewSession} = emqttd_session:store(Session, Message), {Message1, NewSession} = emqttd_session:store(Session, Message),
send(emqttd_message:to_packet(Message1), State#proto_state{session = NewSession}); send(emqttd_message:to_packet(Message1), State#proto_state{session = NewSession});
send(Packet, State = #proto_state{transport = Transport, socket = Sock, peername = Peername, client_id = ClientId}) when is_record(Packet, mqtt_packet) -> send(Packet, State = #proto_state{transport = Transport, socket = Sock, peername = Peername, clientid = ClientId}) when is_record(Packet, mqtt_packet) ->
lager:debug("SENT to ~s@~s: ~s", [ClientId, emqttd_net:format(Peername), emqtt_packet:dump(Packet)]), lager:debug("SENT to ~s@~s: ~s", [ClientId, emqttd_net:format(Peername), emqtt_packet:dump(Packet)]),
sent_stats(Packet), sent_stats(Packet),
Data = emqttd_serialiser:serialise(Packet), Data = emqttd_serialiser:serialise(Packet),
@ -238,7 +242,7 @@ send(Packet, State = #proto_state{transport = Transport, socket = Sock, peername
redeliver({?PUBREL, PacketId}, State) -> redeliver({?PUBREL, PacketId}, State) ->
send(?PUBREL_PACKET(PacketId), State). send(?PUBREL_PACKET(PacketId), State).
shutdown(Error, #proto_state{peername = Peername, client_id = ClientId, will_msg = WillMsg}) -> shutdown(Error, #proto_state{peername = Peername, clientid = ClientId, will_msg = WillMsg}) ->
send_willmsg(WillMsg), send_willmsg(WillMsg),
try_unregister(ClientId, self()), try_unregister(ClientId, self()),
lager:debug("Protocol ~s@~s Shutdown: ~p", [ClientId, emqttd_net:format(Peername), Error]), lager:debug("Protocol ~s@~s Shutdown: ~p", [ClientId, emqttd_net:format(Peername), Error]),
@ -248,13 +252,10 @@ willmsg(Packet) when is_record(Packet, mqtt_packet_connect) ->
emqttd_message:from_packet(Packet). emqttd_message:from_packet(Packet).
clientid(<<>>, #proto_state{peername = Peername}) -> clientid(<<>>, #proto_state{peername = Peername}) ->
<<"eMQTT/", (base64:encode(emqttd_net:format(Peername)))/binary>>; <<"eMQTT_", (base64:encode(emqttd_net:format(Peername)))/binary>>;
clientid(ClientId, _State) -> ClientId. clientid(ClientId, _State) -> ClientId.
mqtt_user(#proto_state{peername = {Addr, _Port}, client_id = ClientId, username = Username}) ->
#mqtt_user{username = Username, clientid = ClientId, ipaddr = Addr}.
send_willmsg(undefined) -> ignore; send_willmsg(undefined) -> ignore;
%%TODO:should call session... %%TODO:should call session...
send_willmsg(WillMsg) -> emqttd_router:route(WillMsg). send_willmsg(WillMsg) -> emqttd_router:route(WillMsg).
@ -282,16 +283,16 @@ 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{clientid = ClientId}, #proto_state{max_clientid_len = MaxLen})
when ( size(ClientId) >= 1 ) andalso ( size(ClientId) =< MaxLen ) -> when ( size(ClientId) >= 1 ) andalso ( size(ClientId) =< MaxLen ) ->
true; true;
%% MQTT3.1.1 allow null clientId. %% MQTT3.1.1 allow null clientId.
validate_clientid(#mqtt_packet_connect{proto_ver =?MQTT_PROTO_V311, client_id = ClientId}, _ProtoState) validate_clientid(#mqtt_packet_connect{proto_ver =?MQTT_PROTO_V311, clientid = ClientId}, _ProtoState)
when size(ClientId) =:= 0 -> when size(ClientId) =:= 0 ->
true; true;
validate_clientid(#mqtt_packet_connect {proto_ver = Ver, clean_sess = CleanSess, client_id = ClientId}, _ProtoState) -> validate_clientid(#mqtt_packet_connect {proto_ver = Ver, clean_sess = CleanSess, clientid = ClientId}, _ProtoState) ->
lager:warning("Invalid ClientId: ~s, ProtoVer: ~p, CleanSess: ~s", [ClientId, Ver, CleanSess]), lager:warning("Invalid ClientId: ~s, ProtoVer: ~p, CleanSess: ~s", [ClientId, Ver, CleanSess]),
false. false.
@ -353,7 +354,7 @@ inc(_) ->
notify(connected, ReturnCode, #proto_state{peername = Peername, notify(connected, ReturnCode, #proto_state{peername = Peername,
proto_ver = ProtoVer, proto_ver = ProtoVer,
client_id = ClientId, clientid = ClientId,
clean_sess = CleanSess}) -> clean_sess = CleanSess}) ->
Sess = case CleanSess of Sess = case CleanSess of
true -> false; true -> false;

View File

@ -128,7 +128,7 @@ subscribe(Topics = [{_Topic, _Qos}|_]) ->
-spec subscribe(Topic :: binary(), Qos :: mqtt_qos()) -> {ok, Qos :: mqtt_qos()}. -spec subscribe(Topic :: binary(), Qos :: mqtt_qos()) -> {ok, Qos :: mqtt_qos()}.
subscribe(Topic, Qos) when is_binary(Topic) andalso ?IS_QOS(Qos) -> subscribe(Topic, Qos) when is_binary(Topic) andalso ?IS_QOS(Qos) ->
TopicRecord = #mqtt_topic{topic = Topic, node = node()}, TopicRecord = #mqtt_topic{topic = Topic, node = node()},
Subscriber = #topic_subscriber{topic = Topic, qos = Qos, subpid = self()}, Subscriber = #mqtt_subscriber{topic = Topic, qos = Qos, subpid = self()},
F = fun() -> F = fun() ->
case insert_topic(TopicRecord) of case insert_topic(TopicRecord) of
ok -> insert_subscriber(Subscriber); ok -> insert_subscriber(Subscriber);
@ -188,7 +188,7 @@ publish(Topic, Msg) when is_binary(Topic) ->
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
-spec dispatch(Topic :: binary(), Msg :: mqtt_message()) -> non_neg_integer(). -spec dispatch(Topic :: binary(), Msg :: mqtt_message()) -> non_neg_integer().
dispatch(Topic, Msg = #mqtt_message{qos = Qos}) when is_binary(Topic) -> dispatch(Topic, Msg = #mqtt_message{qos = Qos}) when is_binary(Topic) ->
case mnesia:dirty_read(topic_subscriber, Topic) of case mnesia:dirty_read(subscriber, Topic) of
[] -> [] ->
%%TODO: not right when clusted... %%TODO: not right when clusted...
setstats(dropped); setstats(dropped);
@ -224,7 +224,7 @@ init([]) ->
process_flag(priority, high), process_flag(priority, high),
process_flag(min_heap_size, 1024*1024), process_flag(min_heap_size, 1024*1024),
mnesia:subscribe({table, topic, simple}), mnesia:subscribe({table, topic, simple}),
mnesia:subscribe({table, topic_subscriber, simple}), mnesia:subscribe({table, subscriber, simple}),
{ok, #state{submap = maps:new()}}. {ok, #state{submap = maps:new()}}.
handle_call(Req, _From, State) -> handle_call(Req, _From, State) ->
@ -265,10 +265,10 @@ handle_info({'DOWN', _Mon, _Type, DownPid, _Info}, State = #state{submap = SubMa
true -> true ->
Node = node(), Node = node(),
F = fun() -> F = fun() ->
Subscribers = mnesia:index_read(topic_subscriber, DownPid, #topic_subscriber.subpid), Subscribers = mnesia:index_read(subscriber, DownPid, #mqtt_subscriber.subpid),
lists:foreach(fun(Sub = #topic_subscriber{topic = Topic}) -> lists:foreach(fun(Sub = #mqtt_subscriber{topic = Topic}) ->
mnesia:delete_object(Sub), mnesia:delete_object(subscriber, Sub, write),
try_remove_topic(#topic{name = Topic, node = Node}) try_remove_topic(#mqtt_topic{topic = Topic, node = Node})
end, Subscribers) end, Subscribers)
end, end,
NewState = NewState =
@ -292,7 +292,7 @@ handle_info(Info, State) ->
terminate(_Reason, _State) -> terminate(_Reason, _State) ->
mnesia:unsubscribe({table, topic, simple}), mnesia:unsubscribe({table, topic, simple}),
mnesia:unsubscribe({table, topic_subscriber, simple}), mnesia:unsubscribe({table, subscriber, simple}),
%%TODO: clear topics belongs to this node??? %%TODO: clear topics belongs to this node???
ok. ok.
@ -302,27 +302,27 @@ code_change(_OldVsn, State, _Extra) ->
%%%============================================================================= %%%=============================================================================
%%% Internal functions %%% Internal functions
%%%============================================================================= %%%=============================================================================
insert_topic(Topic = #topic{name = Name}) -> insert_topic(Record = #mqtt_topic{topic = Topic}) ->
case mnesia:wread({topic, Name}) of case mnesia:wread({topic, Topic}) of
[] -> [] ->
ok = emqttd_trie:insert(Name), ok = emqttd_trie:insert(Topic),
mnesia:write(Topic); mnesia:write(topic, Record, write);
Topics -> Records ->
case lists:member(Topic, Topics) of case lists:member(Record, Records) of
true -> ok; true -> ok;
false -> mnesia:write(Topic) false -> mnesia:write(topic, Record, write)
end end
end. end.
insert_subscriber(Subscriber) -> insert_subscriber(Subscriber) ->
mnesia:write(Subscriber). mnesia:write(subscriber, Subscriber, write).
try_remove_topic(Topic = #topic{name = Name}) -> try_remove_topic(Record = #mqtt_topic{topic = Topic}) ->
case mnesia:read({topic_subscriber, Name}) of case mnesia:read({subscriber, Topic}) of
[] -> [] ->
mnesia:delete_object(Topic), mnesia:delete_object(topic, Record, write),
case mnesia:read(topic, Name) of case mnesia:read(topic, Topic) of
[] -> emqttd_trie:delete(Name); [] -> emqttd_trie:delete(Topic);
_ -> ok _ -> ok
end; end;
_ -> _ ->
@ -335,7 +335,7 @@ setstats(topics) ->
setstats(subscribers) -> setstats(subscribers) ->
emqttd_broker:setstats('subscribers/count', emqttd_broker:setstats('subscribers/count',
'subscribers/max', 'subscribers/max',
mnesia:table_info(topic_subscriber, size)); mnesia:table_info(subscriber, size));
setstats(dropped) -> setstats(dropped) ->
emqttd_metrics:inc('messages/dropped'). emqttd_metrics:inc('messages/dropped').

View File

@ -22,6 +22,8 @@
%%% @doc %%% @doc
%%% emqttd retained messages. %%% emqttd retained messages.
%%% %%%
%%% TODO: need to redesign later.
%%%
%%% @end %%% @end
%%%----------------------------------------------------------------------------- %%%-----------------------------------------------------------------------------
-module(emqttd_retained). -module(emqttd_retained).
@ -30,31 +32,45 @@
-include("emqttd.hrl"). -include("emqttd.hrl").
-define(RETAINED_TABLE, message_retained). %% Mnesia callbacks
-export([mnesia/1]).
-mnesia_create({mnesia, [create]}).
-mnesia_replicate({mnesia, [replicate]}).
%% API Function Exports %% API Function Exports
-export([retain/1, redeliver/2]). -export([retain/1, redeliver/2]).
mnesia(create) ->
ok = emqtt_mnesia:create_table(message, [
{type, ordered_set},
{ram_copies, [node()]},
{record_name, mqtt_message},
{attributes, record_info(fields, mqtt_message)}]);
mnesia(replicate) ->
ok = emqtt_mnesia:copy_table(message).
%% @doc retain message. %% @doc retain message.
-spec retain(mqtt_message()) -> ok | ignore. -spec retain(mqtt_message()) -> ok | ignore.
retain(#mqtt_message{retain = false}) -> ignore; retain(#mqtt_message{retain = false}) -> ignore;
%% RETAIN flag set to 1 and payload containing zero bytes %% RETAIN flag set to 1 and payload containing zero bytes
retain(#mqtt_message{retain = true, topic = Topic, payload = <<>>}) -> retain(#mqtt_message{retain = true, topic = Topic, payload = <<>>}) ->
mnesia:async_dirty(fun mnesia:delete/1, [{?RETAINED_TABLE, Topic}]); mnesia:async_dirty(fun mnesia:delete/1, [{message, Topic}]);
retain(Msg = #mqtt_message{retain = true, retain(Msg = #mqtt_message{topic = Topic,
topic = Topic, retain = true,
qos = Qos, qos = Qos,
payload = Payload}) -> payload = Payload}) ->
TabSize = mnesia:table_info(?RETAINED_TABLE, size), TabSize = mnesia:table_info(message, size),
case {TabSize < limit(table), size(Payload) < limit(payload)} of case {TabSize < limit(table), size(Payload) < limit(payload)} of
{true, true} -> {true, true} ->
lager:debug("Retained: store message: ~p", [Msg]), lager:debug("Retained: store message: ~p", [Msg]),
RetainedMsg = #message_retained{topic = Topic, qos = Qos, payload = Payload}, mnesia:async_dirty(fun mnesia:write/3, [message, Msg, write]),
mnesia:async_dirty(fun mnesia:write/1, [RetainedMsg]),
emqttd_metrics:set('messages/retained/count', emqttd_metrics:set('messages/retained/count',
mnesia:table_info(?RETAINED_TABLE, size)); mnesia:table_info(message, size));
{false, _}-> {false, _}->
lager:error("Dropped retained message(topic=~s) for table is full!", [Topic]); lager:error("Dropped retained message(topic=~s) for table is full!", [Topic]);
{_, false}-> {_, false}->
@ -83,26 +99,23 @@ redeliver(Topics, CPid) when is_pid(CPid) ->
lists:foreach(fun(Topic) -> lists:foreach(fun(Topic) ->
case emqtt_topic:wildcard(Topic) of case emqtt_topic:wildcard(Topic) of
false -> false ->
dispatch(CPid, mnesia:dirty_read(message_retained, Topic)); dispatch(CPid, mnesia:dirty_read(message, Topic));
true -> true ->
Fun = fun(Msg = #message_retained{topic = Name}, Acc) -> Fun = fun(Msg = #mqtt_message{topic = Name}, Acc) ->
case emqtt_topic:match(Name, Topic) of case emqtt_topic:match(Name, Topic) of
true -> [Msg|Acc]; true -> [Msg|Acc];
false -> Acc false -> Acc
end end
end, end,
RetainedMsgs = mnesia:async_dirty(fun mnesia:foldl/3, [Fun, [], ?RETAINED_TABLE]), Msgs = mnesia:async_dirty(fun mnesia:foldl/3, [Fun, [], message]),
dispatch(CPid, lists:reverse(RetainedMsgs)) dispatch(CPid, lists:reverse(Msgs))
end end
end, Topics). end, Topics).
dispatch(_CPid, []) -> dispatch(_CPid, []) ->
ignore; ignore;
dispatch(CPid, RetainedMsgs) when is_list(RetainedMsgs) -> dispatch(CPid, Msgs) when is_list(Msgs) ->
CPid ! {dispatch, {self(), [mqtt_msg(Msg) || Msg <- RetainedMsgs]}}; CPid ! {dispatch, {self(), [Msg || Msg <- Msgs]}};
dispatch(CPid, RetainedMsg) when is_record(RetainedMsg, message_retained) -> dispatch(CPid, Msg) when is_record(Msg, mqtt_message) ->
CPid ! {dispatch, {self(), mqtt_msg(RetainedMsg)}}. CPid ! {dispatch, {self(), Msg}}.
mqtt_msg(#message_retained{topic = Topic, qos = Qos, payload = Payload}) ->
#mqtt_message{qos = Qos, retain = true, topic = Topic, payload = Payload}.

View File

@ -28,6 +28,8 @@
-include("emqttd.hrl"). -include("emqttd.hrl").
-include_lib("emqtt/include/emqtt_packet.hrl").
%% API Function Exports %% API Function Exports
-export([start/1, -export([start/1,
resume/3, resume/3,
@ -47,7 +49,7 @@
terminate/2, code_change/3]). terminate/2, code_change/3]).
-record(session_state, { -record(session_state, {
client_id :: binary(), clientid :: binary(),
client_pid :: pid(), client_pid :: pid(),
message_id = 1, message_id = 1,
submap :: map(), submap :: map(),
@ -122,7 +124,7 @@ publish(SessPid, {?QOS_2, Message}) when is_pid(SessPid) ->
%% @end %% @end
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
-spec puback(session(), {mqtt_packet_type(), mqtt_packet_id()}) -> session(). -spec puback(session(), {mqtt_packet_type(), mqtt_packet_id()}) -> session().
puback(SessState = #session_state{client_id = ClientId, awaiting_ack = Awaiting}, {?PUBACK, PacketId}) -> puback(SessState = #session_state{clientid = ClientId, awaiting_ack = Awaiting}, {?PUBACK, PacketId}) ->
case maps:is_key(PacketId, Awaiting) of case maps:is_key(PacketId, Awaiting) of
true -> ok; true -> ok;
false -> lager:warning("Session ~s: PUBACK PacketId '~p' not found!", [ClientId, PacketId]) false -> lager:warning("Session ~s: PUBACK PacketId '~p' not found!", [ClientId, PacketId])
@ -132,7 +134,7 @@ puback(SessPid, {?PUBACK, PacketId}) when is_pid(SessPid) ->
gen_server:cast(SessPid, {puback, PacketId}), SessPid; gen_server:cast(SessPid, {puback, PacketId}), SessPid;
%% PUBREC %% PUBREC
puback(SessState = #session_state{client_id = ClientId, puback(SessState = #session_state{clientid = ClientId,
awaiting_ack = AwaitingAck, awaiting_ack = AwaitingAck,
awaiting_comp = AwaitingComp}, {?PUBREC, PacketId}) -> awaiting_comp = AwaitingComp}, {?PUBREC, PacketId}) ->
case maps:is_key(PacketId, AwaitingAck) of case maps:is_key(PacketId, AwaitingAck) of
@ -146,7 +148,7 @@ puback(SessPid, {?PUBREC, PacketId}) when is_pid(SessPid) ->
gen_server:cast(SessPid, {pubrec, PacketId}), SessPid; gen_server:cast(SessPid, {pubrec, PacketId}), SessPid;
%% PUBREL %% PUBREL
puback(SessState = #session_state{client_id = ClientId, puback(SessState = #session_state{clientid = ClientId,
awaiting_rel = Awaiting}, {?PUBREL, PacketId}) -> awaiting_rel = Awaiting}, {?PUBREL, PacketId}) ->
case maps:find(PacketId, Awaiting) of case maps:find(PacketId, Awaiting) of
{ok, Msg} -> emqttd_router:route(Msg); {ok, Msg} -> emqttd_router:route(Msg);
@ -158,7 +160,7 @@ puback(SessPid, {?PUBREL, PacketId}) when is_pid(SessPid) ->
gen_server:cast(SessPid, {pubrel, PacketId}), SessPid; gen_server:cast(SessPid, {pubrel, PacketId}), SessPid;
%% PUBCOMP %% PUBCOMP
puback(SessState = #session_state{client_id = ClientId, puback(SessState = #session_state{clientid = ClientId,
awaiting_comp = AwaitingComp}, {?PUBCOMP, PacketId}) -> awaiting_comp = AwaitingComp}, {?PUBCOMP, PacketId}) ->
case maps:is_key(PacketId, AwaitingComp) of case maps:is_key(PacketId, AwaitingComp) of
true -> ok; true -> ok;
@ -176,7 +178,7 @@ puback(SessPid, {?PUBCOMP, PacketId}) when is_pid(SessPid) ->
%% @end %% @end
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
-spec subscribe(session(), [{binary(), mqtt_qos()}]) -> {ok, session(), [mqtt_qos()]}. -spec subscribe(session(), [{binary(), mqtt_qos()}]) -> {ok, session(), [mqtt_qos()]}.
subscribe(SessState = #session_state{client_id = ClientId, submap = SubMap}, Topics) -> subscribe(SessState = #session_state{clientid = ClientId, submap = SubMap}, Topics) ->
Resubs = [Topic || {Name, _Qos} = Topic <- Topics, maps:is_key(Name, SubMap)], Resubs = [Topic || {Name, _Qos} = Topic <- Topics, maps:is_key(Name, SubMap)],
case Resubs of case Resubs of
[] -> ok; [] -> ok;
@ -199,7 +201,7 @@ subscribe(SessPid, Topics) when is_pid(SessPid) ->
%% @end %% @end
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
-spec unsubscribe(session(), [binary()]) -> {ok, session()}. -spec unsubscribe(session(), [binary()]) -> {ok, session()}.
unsubscribe(SessState = #session_state{client_id = ClientId, submap = SubMap}, Topics) -> unsubscribe(SessState = #session_state{clientid = ClientId, submap = SubMap}, Topics) ->
%%TODO: refactor later. %%TODO: refactor later.
case Topics -- maps:keys(SubMap) of case Topics -- maps:keys(SubMap) of
[] -> ok; [] -> ok;
@ -238,7 +240,7 @@ store(SessState = #session_state{message_id = MsgId, awaiting_ack = Awaiting},
{Message1, next_msg_id(SessState#session_state{awaiting_ack = Awaiting1})}. {Message1, next_msg_id(SessState#session_state{awaiting_ack = Awaiting1})}.
initial_state(ClientId) -> initial_state(ClientId) ->
#session_state{client_id = ClientId, #session_state{clientid = ClientId,
submap = #{}, submap = #{},
awaiting_ack = #{}, awaiting_ack = #{},
awaiting_rel = #{}, awaiting_rel = #{},
@ -278,7 +280,7 @@ handle_call(Req, _From, State) ->
{stop, {badreq, Req}, State}. {stop, {badreq, Req}, State}.
handle_cast({resume, ClientId, ClientPid}, State = #session_state{ handle_cast({resume, ClientId, ClientPid}, State = #session_state{
client_id = ClientId, clientid = ClientId,
client_pid = undefined, client_pid = undefined,
msg_queue = Queue, msg_queue = Queue,
awaiting_ack = AwaitingAck, awaiting_ack = AwaitingAck,
@ -328,7 +330,7 @@ handle_cast({pubcomp, PacketId}, State) ->
NewState = puback(State, {?PUBCOMP, PacketId}), NewState = puback(State, {?PUBCOMP, PacketId}),
{noreply, NewState}; {noreply, NewState};
handle_cast({destroy, ClientId}, State = #session_state{client_id = ClientId}) -> handle_cast({destroy, ClientId}, State = #session_state{clientid = ClientId}) ->
lager:warning("Session ~s destroyed", [ClientId]), lager:warning("Session ~s destroyed", [ClientId]),
{stop, normal, State}; {stop, normal, State};
@ -342,14 +344,14 @@ handle_info({dispatch, {_From, Messages}}, State) when is_list(Messages) ->
handle_info({dispatch, {_From, Message}}, State) -> handle_info({dispatch, {_From, Message}}, State) ->
{noreply, dispatch(Message, State)}; {noreply, dispatch(Message, State)};
handle_info({'EXIT', ClientPid, Reason}, State = #session_state{client_id = ClientId, handle_info({'EXIT', ClientPid, Reason}, State = #session_state{clientid = ClientId,
client_pid = ClientPid, client_pid = ClientPid,
expires = Expires}) -> expires = Expires}) ->
lager:warning("Session: client ~s@~p exited, caused by ~p", [ClientId, ClientPid, Reason]), lager:warning("Session: client ~s@~p exited, caused by ~p", [ClientId, ClientPid, Reason]),
Timer = erlang:send_after(Expires * 1000, self(), session_expired), Timer = erlang:send_after(Expires * 1000, self(), session_expired),
{noreply, State#session_state{client_pid = undefined, expire_timer = Timer}}; {noreply, State#session_state{client_pid = undefined, expire_timer = Timer}};
handle_info(session_expired, State = #session_state{client_id = ClientId}) -> handle_info(session_expired, State = #session_state{clientid = ClientId}) ->
lager:warning("Session ~s expired!", [ClientId]), lager:warning("Session ~s expired!", [ClientId]),
{stop, {shutdown, expired}, State}; {stop, {shutdown, expired}, State};
@ -366,7 +368,7 @@ code_change(_OldVsn, State, _Extra) ->
%%% Internal functions %%% Internal functions
%%%============================================================================= %%%=============================================================================
dispatch(Message, State = #session_state{client_id = ClientId, dispatch(Message, State = #session_state{clientid = ClientId,
client_pid = undefined}) -> client_pid = undefined}) ->
queue(ClientId, Message, State); queue(ClientId, Message, State);