Explorar o código

Merge branch 'channel3'

Feng Lee %!s(int64=6) %!d(string=hai) anos
pai
achega
f32a415a83
Modificáronse 95 ficheiros con 2043 adicións e 2721 borrados
  1. 10 10
      include/emqx.hrl
  2. 7 0
      include/emqx_mqtt.hrl
  3. 3 1
      include/types.hrl
  4. 20 13
      src/emqx.erl
  5. 9 6
      src/emqx_access_control.erl
  6. 7 5
      src/emqx_access_rule.erl
  7. 4 1
      src/emqx_acl_cache.erl
  8. 15 11
      src/emqx_alarm_handler.erl
  9. 6 4
      src/emqx_app.erl
  10. 22 18
      src/emqx_banned.erl
  11. 7 5
      src/emqx_base62.erl
  12. 28 23
      src/emqx_batch.erl
  13. 6 1
      src/emqx_bridge.erl
  14. 4 2
      src/emqx_bridge_connect.erl
  15. 5 2
      src/emqx_bridge_mqtt.erl
  16. 3 1
      src/emqx_bridge_msg.erl
  17. 4 1
      src/emqx_bridge_rpc.erl
  18. 4 1
      src/emqx_bridge_sup.erl
  19. 4 2
      src/emqx_broker.erl
  20. 7 5
      src/emqx_broker_helper.erl
  21. 15 13
      src/emqx_broker_sup.erl
  22. 110 70
      src/emqx_channel.erl
  23. 4 1
      src/emqx_cli.erl
  24. 6 3
      src/emqx_client_sock.erl
  25. 323 115
      src/emqx_cm.erl
  26. 4 6
      src/emqx_sm_locker.erl
  27. 55 40
      src/emqx_sm_registry.erl
  28. 27 5
      src/emqx_cm_sup.erl
  29. 3 1
      src/emqx_config.erl
  30. 3 1
      src/emqx_ctl.erl
  31. 16 13
      src/emqx_flapping.erl
  32. 13 9
      src/emqx_frame.erl
  33. 11 7
      src/emqx_gc.erl
  34. 3 1
      src/emqx_gen_mod.erl
  35. 3 1
      src/emqx_guid.erl
  36. 27 15
      src/emqx_hooks.erl
  37. 9 6
      src/emqx_inflight.erl
  38. 7 5
      src/emqx_json.erl
  39. 16 6
      src/emqx_keepalive.erl
  40. 3 1
      src/emqx_kernel_sup.erl
  41. 6 3
      src/emqx_listeners.erl
  42. 8 6
      src/emqx_logger.erl
  43. 16 12
      src/emqx_logger_formatter.erl
  44. 6 3
      src/emqx_logger_handler.erl
  45. 3 1
      src/emqx_message.erl
  46. 13 11
      src/emqx_metrics.erl
  47. 3 1
      src/emqx_misc.erl
  48. 9 7
      src/emqx_mod_acl_internal.erl
  49. 5 3
      src/emqx_mod_presence.erl
  50. 3 1
      src/emqx_mod_rewrite.erl
  51. 3 1
      src/emqx_mod_subscription.erl
  52. 17 6
      src/emqx_mod_sup.erl
  53. 17 10
      src/emqx_modules.erl
  54. 7 5
      src/emqx_mountpoint.erl
  55. 24 7
      src/emqx_mqtt_types.erl
  56. 3 3
      src/emqx_mqtt_caps.erl
  57. 3 1
      src/emqx_mqtt_props.erl
  58. 5 1
      src/emqx_mqueue.erl
  59. 57 44
      src/emqx_os_mon.erl
  60. 8 4
      src/emqx_packet.erl
  61. 3 1
      src/emqx_pd.erl
  62. 7 5
      src/emqx_plugins.erl
  63. 7 4
      src/emqx_pmon.erl
  64. 9 7
      src/emqx_pool.erl
  65. 12 4
      src/emqx_pool_sup.erl
  66. 2 2
      src/emqx_pqueue.erl
  67. 288 342
      src/emqx_protocol.erl
  68. 4 2
      src/emqx_psk.erl
  69. 3 1
      src/emqx_reason_codes.erl
  70. 13 11
      src/emqx_router.erl
  71. 11 9
      src/emqx_router_helper.erl
  72. 3 1
      src/emqx_router_sup.erl
  73. 4 2
      src/emqx_rpc.erl
  74. 7 5
      src/emqx_sequence.erl
  75. 382 971
      src/emqx_session.erl
  76. 0 267
      src/emqx_session_sup.erl
  77. 18 15
      src/emqx_shared_sub.erl
  78. 0 297
      src/emqx_sm.erl
  79. 0 64
      src/emqx_sm_sup.erl
  80. 17 9
      src/emqx_stats.erl
  81. 40 30
      src/emqx_sup.erl
  82. 3 1
      src/emqx_sys.erl
  83. 6 8
      src/emqx_sys_mon.erl
  84. 20 13
      src/emqx_sys_sup.erl
  85. 4 1
      src/emqx_tables.erl
  86. 4 1
      src/emqx_time.erl
  87. 12 5
      src/emqx_topic.erl
  88. 3 1
      src/emqx_tracer.erl
  89. 9 7
      src/emqx_trie.erl
  90. 5 5
      src/emqx_types.erl
  91. 3 1
      src/emqx_vm.erl
  92. 47 31
      src/emqx_vm_mon.erl
  93. 24 11
      src/emqx_ws_channel.erl
  94. 16 10
      src/emqx_zone.erl
  95. 8 16
      test/emqx_ws_channel_SUITE.erl

+ 10 - 10
include/emqx.hrl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -ifndef(EMQ_X_HRL).
 -define(EMQ_X_HRL, true).
@@ -19,10 +21,6 @@
 %% Banner
 %%--------------------------------------------------------------------
 
--define(COPYRIGHT, "Copyright (c) 2013-2019 EMQ Technologies Co., Ltd").
-
--define(LICENSE_MESSAGE, "Licensed under the Apache License, Version 2.0").
-
 -define(PROTOCOL_VERSION, "MQTT/5.0").
 
 -define(ERTS_MINIMUM_REQUIRED, "10.0").
@@ -47,8 +45,6 @@
 %% Message and Delivery
 %%--------------------------------------------------------------------
 
--record(session, {sid, pid}).
-
 -record(subscription, {topic, subid, subopts}).
 
 %% See 'Application Message' in MQTT Version 5.0
@@ -72,9 +68,12 @@
         }).
 
 -record(delivery, {
-          sender  :: pid(),      %% Sender of the delivery
-          message :: #message{}, %% The message delivered
-          results :: list()      %% Dispatches of the message
+          %% Sender of the delivery
+          sender  :: pid(),
+          %% The message delivered
+          message :: #message{},
+          %% Dispatches of the message
+          results :: list()
         }).
 
 %%--------------------------------------------------------------------
@@ -152,6 +151,7 @@
 %%--------------------------------------------------------------------
 %% Banned
 %%--------------------------------------------------------------------
+
 -type(banned_who() ::  {client_id,  binary()}
                      | {username,   binary()}
                      | {ip_address, inet:ip_address()}).

+ 7 - 0
include/emqx_mqtt.hrl

@@ -351,6 +351,13 @@
                  variable = #mqtt_packet_publish{packet_id = PacketId}
                 }).
 
+-define(PUBLISH_PACKET(QoS, Topic, PacketId),
+    #mqtt_packet{header   = #mqtt_packet_header{type = ?PUBLISH,
+                                                qos  = QoS},
+                 variable = #mqtt_packet_publish{topic_name = Topic,
+                                                 packet_id  = PacketId}
+                }).
+
 -define(PUBLISH_PACKET(QoS, Topic, PacketId, Payload),
     #mqtt_packet{header   = #mqtt_packet_header{type = ?PUBLISH,
                                                 qos  = QoS},

+ 3 - 1
include/types.hrl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -type(maybe(T) :: undefined | T).
 

+ 20 - 13
src/emqx.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx).
 
@@ -61,9 +63,13 @@
 
 -define(APP, ?MODULE).
 
-%%------------------------------------------------------------------------------
+-define(COPYRIGHT, "Copyright (c) 2019 EMQ Technologies Co., Ltd").
+
+-define(LICENSE_MESSAGE, "Licensed under the Apache License, Version 2.0").
+
+%%--------------------------------------------------------------------
 %% Bootstrap, is_running...
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 %% @doc Start emqx application
 -spec(start() -> {ok, list(atom())} | {error, term()}).
@@ -95,9 +101,9 @@ is_running(Node) ->
         Pid when is_pid(Pid) -> true
     end.
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% PubSub API
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 -spec(subscribe(emqx_topic:topic() | string()) -> ok).
 subscribe(Topic) ->
@@ -122,9 +128,9 @@ publish(Msg) ->
 unsubscribe(Topic) ->
     emqx_broker:unsubscribe(iolist_to_binary(Topic)).
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% PubSub management API
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 -spec(topics() -> list(emqx_topic:topic())).
 topics() -> emqx_router:topics().
@@ -143,9 +149,9 @@ subscribed(SubPid, Topic) when is_pid(SubPid) ->
 subscribed(SubId, Topic) when is_atom(SubId); is_binary(SubId) ->
     emqx_broker:subscribed(SubId, iolist_to_binary(Topic)).
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% Hooks API
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 -spec(hook(emqx_hooks:hookpoint(), emqx_hooks:action()) -> ok | {error, already_exists}).
 hook(HookPoint, Action) ->
@@ -177,9 +183,9 @@ run_hook(HookPoint, Args) ->
 run_fold_hook(HookPoint, Args, Acc) ->
     emqx_hooks:run_fold(HookPoint, Args, Acc).
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% Shutdown and reboot
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 shutdown() ->
     shutdown(normal).
@@ -193,12 +199,13 @@ shutdown(Reason) ->
 reboot() ->
     lists:foreach(fun application:start/1, [gproc, esockd, ranch, cowboy, ekka, emqx]).
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% Internal functions
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 reload_config(ConfFile) ->
     {ok, [Conf]} = file:consult(ConfFile),
     lists:foreach(fun({App, Vals}) ->
                       [application:set_env(App, Par, Val) || {Par, Val} <- Vals]
                   end, Conf).
+

+ 9 - 6
src/emqx_access_control.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_access_control).
 
@@ -22,9 +24,10 @@
         , reload_acl/0
         ]).
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% APIs
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
+
 -spec(authenticate(emqx_types:credentials())
       -> {ok, emqx_types:credentials()} | {error, term()}).
 authenticate(Credentials) ->
@@ -39,7 +42,8 @@ authenticate(Credentials) ->
 	end.
 
 %% @doc Check ACL
--spec(check_acl(emqx_types:credentials(), emqx_types:pubsub(), emqx_types:topic()) -> allow | deny).
+-spec(check_acl(emqx_types:credentials(), emqx_types:pubsub(), emqx_types:topic())
+      -> allow | deny).
 check_acl(Credentials, PubSub, Topic) ->
     case emqx_acl_cache:is_enabled() of
         false ->
@@ -50,8 +54,7 @@ check_acl(Credentials, PubSub, Topic) ->
                     AclResult = do_check_acl(Credentials, PubSub, Topic),
                     emqx_acl_cache:put_acl_cache(PubSub, Topic, AclResult),
                     AclResult;
-                AclResult ->
-                    AclResult
+                AclResult -> AclResult
             end
     end.
 

+ 7 - 5
src/emqx_access_rule.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_access_rule).
 
@@ -21,6 +23,8 @@
         , compile/1
         ]).
 
+-export_type([rule/0]).
+
 -type(acl_result() :: allow | deny).
 
 -type(who() :: all | binary() |
@@ -33,14 +37,12 @@
 -type(rule() :: {acl_result(), all} |
                 {acl_result(), who(), access(), list(emqx_topic:topic())}).
 
--export_type([rule/0]).
-
 -define(ALLOW_DENY(A), ((A =:= allow) orelse (A =:= deny))).
 -define(PUBSUB(A), ((A =:= subscribe) orelse (A =:= publish) orelse (A =:= pubsub))).
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% APIs
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 %% @doc Compile Access Rule.
 compile({A, all}) when ?ALLOW_DENY(A) ->

+ 4 - 1
src/emqx_acl_cache.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_acl_cache).
 
@@ -124,6 +126,7 @@ map_acl_cache(Fun) ->
 %%--------------------------------------------------------------------
 %% Internal functions
 %%--------------------------------------------------------------------
+
 add_acl(PubSub, Topic, AclResult) ->
     K = cache_k(PubSub, Topic),
     V = cache_v(AclResult),

+ 15 - 11
src/emqx_alarm_handler.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_alarm_handler).
 
@@ -46,9 +48,9 @@
 -define(ALARM_TAB, emqx_alarm).
 -define(ALARM_HISTORY_TAB, emqx_alarm_history).
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% Mnesia bootstrap
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 mnesia(boot) ->
     ok = ekka_mnesia:create_table(?ALARM_TAB, [
@@ -63,13 +65,14 @@ mnesia(boot) ->
                 {local_content, true},
                 {record_name, alarm_history},
                 {attributes, record_info(fields, alarm_history)}]);
+
 mnesia(copy) ->
     ok = ekka_mnesia:copy_table(?ALARM_TAB),
     ok = ekka_mnesia:copy_table(?ALARM_HISTORY_TAB).
 
-%%----------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% API
-%%----------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 load() ->
     gen_event:swap_handler(alarm_handler, {alarm_handler, swap}, {?MODULE, []}).
@@ -81,13 +84,14 @@ unload() ->
 get_alarms() ->
     gen_event:call(alarm_handler, ?MODULE, get_alarms).
 
-%%----------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% gen_event callbacks
-%%----------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 init({_Args, {alarm_handler, ExistingAlarms}}) ->
     init_tables(ExistingAlarms),
     {ok, []};
+
 init(_) ->
     init_tables([]),
     {ok, []}.
@@ -138,9 +142,9 @@ init_tables(ExistingAlarms) ->
                       set_alarm_history(Id)
                   end, ExistingAlarms).
 
-encode_alarm({AlarmId, #alarm{severity  = Severity, 
+encode_alarm({AlarmId, #alarm{severity  = Severity,
                               title     = Title,
-                              summary   = Summary, 
+                              summary   = Summary,
                               timestamp = Ts}}) ->
     emqx_json:safe_encode([{id, maybe_to_binary(AlarmId)},
                            {desc, [{severity, Severity},
@@ -180,6 +184,6 @@ get_alarms_() ->
     [{Id, Desc} || #common_alarm{id = Id, desc = Desc} <- Alarms].
 
 set_alarm_history(Id) ->
-    mnesia:dirty_write(?ALARM_HISTORY_TAB, #alarm_history{id = Id,
-                                                          clear_at = undefined}).
+    His = #alarm_history{id = Id, clear_at = undefined},
+    mnesia:dirty_write(?ALARM_HISTORY_TAB, His).
 

+ 6 - 4
src/emqx_app.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_app).
 
@@ -30,10 +32,10 @@ start(_Type, _Args) ->
     print_banner(),
     ekka:start(),
     {ok, Sup} = emqx_sup:start_link(),
-    emqx_modules:load(),
-    emqx_plugins:init(),
+    ok = emqx_modules:load(),
+    ok = emqx_plugins:init(),
     emqx_plugins:load(),
-    emqx_listeners:start(),
+    ok = emqx_listeners:start(),
     start_autocluster(),
     register(emqx, self()),
 

+ 22 - 18
src/emqx_banned.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_banned).
 
@@ -44,14 +46,14 @@
         , code_change/3
         ]).
 
--define(TAB, ?MODULE).
+-define(BANNED_TAB, ?MODULE).
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% Mnesia bootstrap
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 mnesia(boot) ->
-    ok = ekka_mnesia:create_table(?TAB, [
+    ok = ekka_mnesia:create_table(?BANNED_TAB, [
                 {type, set},
                 {disc_copies, [node()]},
                 {record_name, banned},
@@ -59,7 +61,7 @@ mnesia(boot) ->
                 {storage_properties, [{ets, [{read_concurrency, true}]}]}]);
 
 mnesia(copy) ->
-    ok = ekka_mnesia:copy_table(?TAB).
+    ok = ekka_mnesia:copy_table(?BANNED_TAB).
 
 %% @doc Start the banned server.
 -spec(start_link() -> startlink_ret()).
@@ -68,23 +70,23 @@ start_link() ->
 
 -spec(check(emqx_types:credentials()) -> boolean()).
 check(#{client_id := ClientId, username := Username, peername := {IPAddr, _}}) ->
-    ets:member(?TAB, {client_id, ClientId})
-        orelse ets:member(?TAB, {username, Username})
-            orelse ets:member(?TAB, {ipaddr, IPAddr}).
+    ets:member(?BANNED_TAB, {client_id, ClientId})
+        orelse ets:member(?BANNED_TAB, {username, Username})
+            orelse ets:member(?BANNED_TAB, {ipaddr, IPAddr}).
 
 -spec(add(emqx_types:banned()) -> ok).
 add(Banned) when is_record(Banned, banned) ->
-    mnesia:dirty_write(?TAB, Banned).
+    mnesia:dirty_write(?BANNED_TAB, Banned).
 
 -spec(delete({client_id, emqx_types:client_id()}
              | {username, emqx_types:username()}
              | {peername, emqx_types:peername()}) -> ok).
 delete(Key) ->
-    mnesia:dirty_delete(?TAB, Key).
+    mnesia:dirty_delete(?BANNED_TAB, Key).
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% gen_server callbacks
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 init([]) ->
     {ok, ensure_expiry_timer(#{expiry_timer => undefined})}.
@@ -98,7 +100,8 @@ handle_cast(Msg, State) ->
     {noreply, State}.
 
 handle_info({timeout, TRef, expire}, State = #{expiry_timer := TRef}) ->
-    mnesia:async_dirty(fun expire_banned_items/1, [erlang:system_time(second)]),
+    mnesia:async_dirty(fun expire_banned_items/1,
+                       [erlang:system_time(second)]),
     {noreply, ensure_expiry_timer(State), hibernate};
 
 handle_info(Info, State) ->
@@ -111,9 +114,9 @@ terminate(_Reason, #{expiry_timer := TRef}) ->
 code_change(_OldVsn, State, _Extra) ->
     {ok, State}.
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% Internal functions
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 -ifdef(TEST).
 ensure_expiry_timer(State) ->
@@ -126,6 +129,7 @@ ensure_expiry_timer(State) ->
 expire_banned_items(Now) ->
     mnesia:foldl(
       fun(B = #banned{until = Until}, _Acc) when Until < Now ->
-              mnesia:delete_object(?TAB, B, sticky_write);
+              mnesia:delete_object(?BANNED_TAB, B, sticky_write);
          (_, _Acc) -> ok
-      end, ok, ?TAB).
+      end, ok, ?BANNED_TAB).
+

+ 7 - 5
src/emqx_base62.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_base62).
 
@@ -21,9 +23,9 @@
         , decode/2
         ]).
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% APIs
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 %% @doc Encode any data to base62 binary
 -spec encode(string() | integer() | binary()) -> binary().
@@ -43,9 +45,9 @@ decode(L) when is_list(L) ->
 decode(B) when is_binary(B) ->
     decode(B, <<>>).
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% Interval Functions
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 encode(D, string) ->
     binary_to_list(encode(D));

+ 28 - 23
src/emqx_batch.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_batch).
 
@@ -22,46 +24,49 @@
         , items/1
         ]).
 
--record(batch,
-        { batch_size :: non_neg_integer()
-        , batch_q :: list(any())
-        , linger_ms :: pos_integer()
-        , linger_timer :: reference() | undefined
-        , commit_fun :: function()
-        }).
-
--type(options() ::
-      #{ batch_size => non_neg_integer()
-       , linger_ms => pos_integer()
-       , commit_fun := function()
-       }).
+-export_type([options/0, batch/0]).
 
--opaque(batch() :: #batch{}).
+-record(batch, {
+          batch_size :: non_neg_integer(),
+          batch_q :: list(any()),
+          linger_ms :: pos_integer(),
+          linger_timer :: reference() | undefined,
+          commit_fun :: function()
+         }).
 
--export_type([options/0]).
+-type(options() :: #{
+        batch_size => non_neg_integer(),
+        linger_ms => pos_integer(),
+        commit_fun := function()
+       }).
 
--export_type([batch/0]).
+-opaque(batch() :: #batch{}).
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% APIs
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 -spec(init(options()) -> batch()).
 init(Opts) when is_map(Opts) ->
     #batch{batch_size = maps:get(batch_size, Opts, 1000),
            batch_q = [],
-           linger_ms = maps:get(linger_ms, Opts, 1000),
+           linger_ms  = maps:get(linger_ms, Opts, 1000),
            commit_fun = maps:get(commit_fun, Opts)}.
 
 -spec(push(any(), batch()) -> batch()).
-push(El, Batch = #batch{batch_q = Q, linger_ms = Ms, linger_timer = undefined}) when length(Q) == 0 ->
-    Batch#batch{batch_q = [El], linger_timer = erlang:send_after(Ms, self(), batch_linger_expired)};
+push(El, Batch = #batch{batch_q = Q,
+                        linger_ms = Ms,
+                        linger_timer = undefined})
+  when length(Q) == 0 ->
+    TRef = erlang:send_after(Ms, self(), batch_linger_expired),
+    Batch#batch{batch_q = [El], linger_timer = TRef};
 
 %% no limit.
 push(El, Batch = #batch{batch_size = 0, batch_q = Q}) ->
     Batch#batch{batch_q = [El|Q]};
 
-push(El, Batch = #batch{batch_size = MaxSize, batch_q = Q}) when length(Q) >= MaxSize ->
+push(El, Batch = #batch{batch_size = MaxSize, batch_q = Q})
+  when length(Q) >= MaxSize ->
     commit(Batch#batch{batch_q = [El|Q]});
 
 push(El, Batch = #batch{batch_q = Q}) ->

+ 6 - 1
src/emqx_bridge.erl

@@ -1,3 +1,4 @@
+%%--------------------------------------------------------------------
 %% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
@@ -11,7 +12,9 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
+%%--------------------------------------------------------------------
 %% @doc Bridge works in two layers (1) batching layer (2) transport layer
 %% The `bridge' batching layer collects local messages in batches and sends over
 %% to remote MQTT node/cluster via `connetion' transport layer.
@@ -56,8 +59,10 @@
 %%
 %% NOTES:
 %% * Local messages are all normalised to QoS-1 when exporting to remote
+%%--------------------------------------------------------------------
 
 -module(emqx_bridge).
+
 -behaviour(gen_statem).
 
 %% APIs
@@ -104,7 +109,7 @@
              ]).
 
 -type id() :: atom() | string() | pid().
--type qos() :: emqx_mqtt_types:qos().
+-type qos() :: emqx_mqtt:qos().
 -type config() :: map().
 -type batch() :: [emqx_bridge_msg:exp_msg()].
 -type ack_ref() :: term().

+ 4 - 2
src/emqx_bridge_connect.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_bridge_connect).
 
@@ -27,7 +29,7 @@
 -type batch() :: emqx_protal:batch().
 -type ack_ref() :: emqx_bridge:ack_ref().
 -type topic() :: emqx_topic:topic().
--type qos() :: emqx_mqtt_types:qos().
+-type qos() :: emqx_mqtt:qos().
 
 -include("logger.hrl").
 

+ 5 - 2
src/emqx_bridge_mqtt.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,8 +12,10 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
-%% @doc This module implements EMQX Bridge transport layer on top of MQTT protocol
+%% @doc This module implements EMQX Bridge transport layer on top of
+%% MQTT protocol.
 
 -module(emqx_bridge_mqtt).
 

+ 3 - 1
src/emqx_bridge_msg.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_bridge_msg).
 

+ 4 - 1
src/emqx_bridge_rpc.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,10 +12,12 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 %% @doc This module implements EMQX Bridge transport layer based on gen_rpc.
 
 -module(emqx_bridge_rpc).
+
 -behaviour(emqx_bridge_connect).
 
 %% behaviour callbacks

+ 4 - 1
src/emqx_bridge_sup.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,8 +12,10 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_bridge_sup).
+
 -behavior(supervisor).
 
 -include("logger.hrl").

+ 4 - 2
src/emqx_broker.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_broker).
 
@@ -284,7 +286,7 @@ dispatch(Topic, Delivery = #delivery{message = Msg, results = Results}) ->
 dispatch(SubPid, Topic, Msg) when is_pid(SubPid) ->
     case erlang:is_process_alive(SubPid) of
         true ->
-            SubPid ! {dispatch, Topic, Msg},
+            SubPid ! {deliver, Topic, Msg},
             1;
         false -> 0
     end;

+ 7 - 5
src/emqx_broker_helper.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_broker_helper).
 
@@ -92,9 +94,9 @@ create_seq(Topic) ->
 reclaim_seq(Topic) ->
     emqx_sequence:reclaim(?SUBSEQ, Topic).
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% gen_server callbacks
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 init([]) ->
     %% Helper table
@@ -142,9 +144,9 @@ terminate(_Reason, _State) ->
 code_change(_OldVsn, State, _Extra) ->
     {ok, State}.
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% Internal functions
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 clean_down(SubPid) ->
     case ets:lookup(?SUBMON, SubPid) of

+ 15 - 13
src/emqx_broker_sup.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_broker_sup).
 
@@ -23,9 +25,9 @@
 start_link() ->
     supervisor:start_link({local, ?MODULE}, ?MODULE, []).
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% Supervisor callbacks
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 init([]) ->
     %% Broker pool
@@ -34,20 +36,20 @@ init([]) ->
                                      {emqx_broker, start_link, []}]),
 
     %% Shared subscription
-    SharedSub = #{id       => shared_sub,
-                  start    => {emqx_shared_sub, start_link, []},
-                  restart  => permanent,
+    SharedSub = #{id => shared_sub,
+                  start => {emqx_shared_sub, start_link, []},
+                  restart => permanent,
                   shutdown => 2000,
-                  type     => worker,
-                  modules  => [emqx_shared_sub]},
+                  type => worker,
+                  modules => [emqx_shared_sub]},
 
     %% Broker helper
-    Helper = #{id       => helper,
-               start    => {emqx_broker_helper, start_link, []},
-               restart  => permanent,
+    Helper = #{id => helper,
+               start => {emqx_broker_helper, start_link, []},
+               restart => permanent,
                shutdown => 2000,
-               type     => worker,
-               modules  => [emqx_broker_helper]},
+               type => worker,
+               modules => [emqx_broker_helper]},
 
     {ok, {{one_for_all, 0, 1}, [BrokerPool, SharedSub, Helper]}}.
 

+ 110 - 70
src/emqx_channel.erl

@@ -14,6 +14,7 @@
 %% limitations under the License.
 %%--------------------------------------------------------------------
 
+%% MQTT TCP/SSL Channel
 -module(emqx_channel).
 
 -behaviour(gen_statem).
@@ -21,6 +22,7 @@
 -include("emqx.hrl").
 -include("emqx_mqtt.hrl").
 -include("logger.hrl").
+-include("types.hrl").
 
 -logger_header("[Channel]").
 
@@ -32,13 +34,10 @@
         , stats/1
         ]).
 
--export([kick/1]).
-
--export([session/1]).
-
 %% gen_statem callbacks
 -export([ idle/3
         , connected/3
+        , disconnected/3
         ]).
 
 -export([ init/1
@@ -48,28 +47,32 @@
         ]).
 
 -record(state, {
-          transport,
-          socket,
-          peername,
-          sockname,
-          conn_state,
-          active_n,
-          proto_state,
-          parse_state,
-          gc_state,
-          keepalive,
-          rate_limit,
-          pub_limit,
-          limit_timer,
-          enable_stats,
-          stats_timer,
-          idle_timeout
+          transport    :: esockd:transport(),
+          socket       :: esockd:sock(),
+          peername     :: {inet:ip_address(), inet:port_number()},
+          sockname     :: {inet:ip_address(), inet:port_number()},
+          conn_state   :: running | blocked,
+          active_n     :: pos_integer(),
+          rate_limit   :: maybe(esockd_rate_limit:bucket()),
+          pub_limit    :: maybe(esockd_rate_limit:bucket()),
+          limit_timer  :: maybe(reference()),
+          serializer   :: emqx_frame:serializer(), %% TODO: remove it later.
+          parse_state  :: emqx_frame:parse_state(),
+          proto_state  :: emqx_protocol:protocol(),
+          gc_state     :: emqx_gc:gc_state(),
+          keepalive    :: maybe(reference()),
+          enable_stats :: boolean(),
+          stats_timer  :: maybe(reference()),
+          idle_timeout :: timeout()
          }).
 
 -define(ACTIVE_N, 100).
 -define(HANDLE(T, C, D), handle((T), (C), (D))).
+-define(CHAN_STATS, [recv_pkt, recv_msg, send_pkt, send_msg]).
 -define(SOCK_STATS, [recv_oct, recv_cnt, send_oct, send_cnt, send_pend]).
 
+-spec(start_link(esockd:transport(), esockd:sock(), proplists:proplist())
+      -> {ok, pid()}).
 start_link(Transport, Socket, Options) ->
     {ok, proc_lib:spawn_link(?MODULE, init, [{Transport, Socket, Options}])}.
 
@@ -123,22 +126,13 @@ attrs(#state{peername = Peername,
 stats(CPid) when is_pid(CPid) ->
     call(CPid, stats);
 
-stats(#state{transport = Transport,
-             socket = Socket,
-             proto_state = ProtoState}) ->
+stats(#state{transport = Transport, socket = Socket}) ->
     SockStats = case Transport:getstat(Socket, ?SOCK_STATS) of
                     {ok, Ss}   -> Ss;
                     {error, _} -> []
                 end,
-    lists:append([SockStats,
-                  emqx_misc:proc_stats(),
-                  emqx_protocol:stats(ProtoState)]).
-
-kick(CPid) ->
-    call(CPid, kick).
-
-session(CPid) ->
-    call(CPid, session).
+    ChanStats = [{Name, emqx_pd:get_counter(Name)} || Name <- ?CHAN_STATS],
+    lists:append([SockStats, ChanStats, emqx_misc:proc_stats()]).
 
 call(CPid, Req) ->
     gen_statem:call(CPid, Req, infinity).
@@ -157,23 +151,15 @@ init({Transport, RawSocket, Options}) ->
     RateLimit = init_limiter(proplists:get_value(rate_limit, Options)),
     PubLimit = init_limiter(emqx_zone:get_env(Zone, publish_limit)),
     ActiveN = proplists:get_value(active_n, Options, ?ACTIVE_N),
-    SendFun = fun(Packet, Opts) ->
-                      Data = emqx_frame:serialize(Packet, Opts),
-                      case Transport:async_send(Socket, Data) of
-                          ok -> {ok, Data};
-                          {error, Reason} ->
-                              {error, Reason}
-                      end
-              end,
+    MaxSize = emqx_zone:get_env(Zone, max_packet_size, ?MAX_PACKET_SIZE),
+    ParseState = emqx_frame:initial_parse_state(#{max_size => MaxSize}),
     ProtoState = emqx_protocol:init(#{peername => Peername,
                                       sockname => Sockname,
                                       peercert => Peercert,
-                                      sendfun  => SendFun,
                                       conn_mod => ?MODULE}, Options),
-    MaxSize = emqx_zone:get_env(Zone, max_packet_size, ?MAX_PACKET_SIZE),
-    ParseState = emqx_frame:initial_parse_state(#{max_size => MaxSize}),
     GcPolicy = emqx_zone:get_env(Zone, force_gc_policy, false),
     GcState = emqx_gc:init(GcPolicy),
+    ok = emqx_misc:init_proc_mng_policy(Zone),
     EnableStats = emqx_zone:get_env(Zone, enable_stats, true),
     IdleTimout = emqx_zone:get_env(Zone, idle_timeout, 30000),
     State = #state{transport    = Transport,
@@ -183,13 +169,12 @@ init({Transport, RawSocket, Options}) ->
                    active_n     = ActiveN,
                    rate_limit   = RateLimit,
                    pub_limit    = PubLimit,
-                   proto_state  = ProtoState,
                    parse_state  = ParseState,
+                   proto_state  = ProtoState,
                    gc_state     = GcState,
                    enable_stats = EnableStats,
                    idle_timeout = IdleTimout
                   },
-    ok = emqx_misc:init_proc_mng_policy(Zone),
     gen_statem:enter_loop(?MODULE, [{hibernate_after, 2 * IdleTimout}],
                           idle, State, self(), [IdleTimout]).
 
@@ -209,12 +194,17 @@ idle(enter, _, State) ->
     keep_state_and_data;
 
 idle(timeout, _Timeout, State) ->
-    {stop, idle_timeout, State};
+    stop(idle_timeout, State);
+
+idle(cast, {incoming, Packet = ?CONNECT_PACKET(ConnVar)}, State) ->
+    #mqtt_packet_connect{proto_ver = ProtoVer} = ConnVar,
+    Serializer = emqx_frame:init_serializer(#{version => ProtoVer}),
+    NState = State#state{serializer = Serializer},
+    handle_incoming(Packet, fun(St) -> {next_state, connected, St} end, NState);
 
 idle(cast, {incoming, Packet}, State) ->
-    handle_incoming(Packet, fun(NState) ->
-                                    {next_state, connected, NState}
-                            end, State);
+    ?LOG(warning, "Unexpected incoming: ~p", [Packet]),
+    shutdown(unexpected_incoming_packet, State);
 
 idle(EventType, Content, State) ->
     ?HANDLE(EventType, Content, State).
@@ -226,18 +216,23 @@ connected(enter, _, _State) ->
     %% What to do?
     keep_state_and_data;
 
-%% Handle Input
+connected(cast, {incoming, Packet = ?PACKET(?CONNECT)}, State) ->
+    ?LOG(warning, "Unexpected connect: ~p", [Packet]),
+    shutdown(unexpected_incoming_connect, State);
+
 connected(cast, {incoming, Packet = ?PACKET(Type)}, State) ->
     ok = emqx_metrics:inc_recv(Packet),
     (Type == ?PUBLISH) andalso emqx_pd:update_counter(incoming_pubs, 1),
-    handle_incoming(Packet, fun(NState) -> {keep_state, NState} end, State);
+    handle_incoming(Packet, fun(St) -> {keep_state, St} end, State);
 
-%% Handle Output
-connected(info, {deliver, PubOrAck}, State = #state{proto_state = ProtoState}) ->
-    case emqx_protocol:deliver(PubOrAck, ProtoState) of
+%% Handle delivery
+connected(info, Devliery = {deliver, _Topic, Msg}, State = #state{proto_state = ProtoState}) ->
+    case emqx_protocol:handle_out(Devliery, ProtoState) of
         {ok, NProtoState} ->
+            {keep_state, State#state{proto_state = NProtoState}};
+        {ok, Packet, NProtoState} ->
             NState = State#state{proto_state = NProtoState},
-            {keep_state, maybe_gc(PubOrAck, NState)};
+            handle_outgoing(Packet, fun(St) -> {keep_state, St} end, NState);
         {error, Reason} ->
             shutdown(Reason, State)
     end;
@@ -272,6 +267,16 @@ connected(info, {keepalive, check}, State = #state{keepalive = KeepAlive}) ->
 connected(EventType, Content, State) ->
     ?HANDLE(EventType, Content, State).
 
+%%--------------------------------------------------------------------
+%% Disconnected State
+
+disconnected(enter, _, _State) ->
+    %% TODO: What to do?
+    keep_state_and_data;
+
+disconnected(EventType, Content, State) ->
+    ?HANDLE(EventType, Content, State).
+
 %% Handle call
 handle({call, From}, info, State) ->
     reply(From, info(State), State);
@@ -286,8 +291,9 @@ handle({call, From}, kick, State) ->
     ok = gen_statem:reply(From, ok),
     shutdown(kicked, State);
 
-handle({call, From}, session, State = #state{proto_state = ProtoState}) ->
-    reply(From, emqx_protocol:session(ProtoState), State);
+handle({call, From}, discard, State) ->
+    ok = gen_statem:reply(From, ok),
+    shutdown(discard, State);
 
 handle({call, From}, Req, State) ->
     ?LOG(error, "Unexpected call: ~p", [Req]),
@@ -299,7 +305,8 @@ handle(cast, Msg, State) ->
     {keep_state, State};
 
 %% Handle Incoming
-handle(info, {Inet, _Sock, Data}, State) when Inet == tcp; Inet == ssl ->
+handle(info, {Inet, _Sock, Data}, State) when Inet == tcp;
+                                              Inet == ssl ->
     Oct = iolist_size(Data),
     ?LOG(debug, "RECV ~p", [Data]),
     emqx_pd:update_counter(incoming_bytes, Oct),
@@ -337,7 +344,7 @@ handle(info, {inet_reply, _Sock, {error, Reason}}, State) ->
 handle(info, {timeout, Timer, emit_stats},
        State = #state{stats_timer = Timer,
                       proto_state = ProtoState,
-                      gc_state = GcState}) ->
+                      gc_state    = GcState}) ->
     ClientId = emqx_protocol:client_id(ProtoState),
     emqx_cm:set_conn_stats(ClientId, stats(State)),
     NState = State#state{stats_timer = undefined},
@@ -377,15 +384,9 @@ terminate(Reason, _StateName, #state{transport = Transport,
                                      keepalive = KeepAlive,
                                      proto_state = ProtoState}) ->
     ?LOG(debug, "Terminated for ~p", [Reason]),
-    Transport:fast_close(Socket),
-    emqx_keepalive:cancel(KeepAlive),
-    case {ProtoState, Reason} of
-        {undefined, _} -> ok;
-        {_, {shutdown, Error}} ->
-            emqx_protocol:terminate(Error, ProtoState);
-        {_, Reason} ->
-            emqx_protocol:terminate(Reason, ProtoState)
-    end.
+    ok = Transport:fast_close(Socket),
+    ok = emqx_keepalive:cancel(KeepAlive),
+    emqx_protocol:terminate(Reason, ProtoState).
 
 %%--------------------------------------------------------------------
 %% Process incoming data
@@ -418,10 +419,16 @@ next_events(Packet) ->
 %%--------------------------------------------------------------------
 %% Handle incoming packet
 
-handle_incoming(Packet, SuccFun, State = #state{proto_state = ProtoState}) ->
-    case emqx_protocol:received(Packet, ProtoState) of
+handle_incoming(Packet = ?PACKET(Type), SuccFun,
+                State = #state{proto_state = ProtoState}) ->
+    _ = inc_incoming_stats(Type),
+    ?LOG(debug, "RECV ~s", [emqx_packet:format(Packet)]),
+    case emqx_protocol:handle_in(Packet, ProtoState) of
         {ok, NProtoState} ->
             SuccFun(State#state{proto_state = NProtoState});
+        {ok, OutPacket, NProtoState} ->
+            handle_outgoing(OutPacket, SuccFun,
+                            State#state{proto_state = NProtoState});
         {error, Reason} ->
             shutdown(Reason, State);
         {error, Reason, NProtoState} ->
@@ -430,6 +437,22 @@ handle_incoming(Packet, SuccFun, State = #state{proto_state = ProtoState}) ->
             stop(Error, State#state{proto_state = NProtoState})
     end.
 
+%%--------------------------------------------------------------------
+%% Handle outgoing packet
+
+handle_outgoing(Packet = ?PACKET(Type), SuccFun,
+                State = #state{transport = Transport,
+                               socket = Socket,
+                               serializer = Serializer}) ->
+    _ = inc_outgoing_stats(Type),
+    ?LOG(debug, "SEND ~s", [emqx_packet:format(Packet)]),
+    Data = Serializer(Packet),
+    case Transport:async_send(Socket, Data) of
+        ok -> SuccFun(State);
+        {error, Reason} ->
+            shutdown(Reason, State)
+    end.
+
 %%--------------------------------------------------------------------
 %% Ensure rate limit
 
@@ -452,6 +475,12 @@ ensure_rate_limit([{Rl, Pos, Cnt}|Limiters], State) ->
            setelement(Pos, State#state{conn_state = blocked, limit_timer = TRef}, Rl1)
    end.
 
+%% start_keepalive(0, _PState) ->
+%%     ignore;
+%% start_keepalive(Secs, #pstate{zone = Zone}) when Secs > 0 ->
+%%     Backoff = emqx_zone:get_env(Zone, keepalive_backoff, 0.75),
+%%     self() ! {keepalive, start, round(Secs * Backoff)}.
+
 %%--------------------------------------------------------------------
 %% Activate socket
 
@@ -466,6 +495,17 @@ activate_socket(#state{transport = Transport, socket = Socket, active_n = N}) ->
             ok
     end.
 
+%%--------------------------------------------------------------------
+%% Inc incoming/outgoing stats
+
+inc_incoming_stats(Type) ->
+    emqx_pd:update_counter(recv_pkt, 1),
+    Type =:= ?PUBLISH andalso emqx_pd:update_counter(recv_msg, 1).
+
+inc_outgoing_stats(Type) ->
+    emqx_pd:update_counter(send_pkt, 1),
+    Type =:= ?PUBLISH andalso emqx_pd:update_counter(send_msg, 1).
+
 %%--------------------------------------------------------------------
 %% Ensure stats timer
 

+ 4 - 1
src/emqx_cli.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_cli).
 
@@ -35,3 +37,4 @@ usage(CmdList) ->
 
 usage(Format, Args) ->
     usage([{Format, Args}]).
+

+ 6 - 3
src/emqx_client_sock.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_client_sock).
 
@@ -24,6 +26,8 @@
         , getstat/2
         ]).
 
+-export_type([socket/0, option/0]).
+
 -record(ssl_socket, {tcp, ssl}).
 
 -type(socket() :: inet:socket() | #ssl_socket{}).
@@ -32,8 +36,6 @@
 
 -type(option() :: gen_tcp:connect_option() | {ssl_opts, [ssl:ssl_option()]}).
 
--export_type([socket/0, option/0]).
-
 -define(DEFAULT_TCP_OPTIONS, [binary, {packet, raw}, {active, false},
                               {nodelay, true}, {reuseaddr, true}]).
 
@@ -105,3 +107,4 @@ default_ciphers(TlsVersions) ->
         fun(TlsVer, Ciphers) ->
             Ciphers ++ ssl:cipher_suites(all, TlsVer)
         end, [], TlsVersions).
+

+ 323 - 115
src/emqx_cm.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,7 +12,9 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
+%% Channel Manager
 -module(emqx_cm).
 
 -behaviour(gen_server).
@@ -24,25 +27,39 @@
 
 -export([start_link/0]).
 
--export([ register_connection/1
-        , register_connection/2
-        , unregister_connection/1
-        , unregister_connection/2
+-export([ register_channel/1
+        , unregister_channel/1
+        , unregister_channel/2
         ]).
 
 -export([ get_conn_attrs/1
         , get_conn_attrs/2
         , set_conn_attrs/2
-        , set_conn_attrs/3
         ]).
 
 -export([ get_conn_stats/1
         , get_conn_stats/2
         , set_conn_stats/2
-        , set_conn_stats/3
         ]).
 
--export([lookup_conn_pid/1]).
+-export([ open_session/1
+        , discard_session/1
+        , resume_session/1
+        ]).
+
+-export([ get_session_attrs/1
+        , get_session_attrs/2
+        , set_session_attrs/2
+        ]).
+
+-export([ get_session_stats/1
+        , get_session_stats/2
+        , set_session_stats/2
+        ]).
+
+-export([ lookup_channels/1
+        , lookup_channels/2
+        ]).
 
 %% gen_server callbacks
 -export([ init/1
@@ -53,159 +70,350 @@
         , code_change/3
         ]).
 
-%% internal export
+%% Internal export
 -export([stats_fun/0]).
 
--define(CM, ?MODULE).
+-export_type([attrs/0, stats/0]).
+
+-type(chan_pid() :: pid()).
 
-%% ETS tables for connection management.
--define(CONN_TAB, emqx_conn).
--define(CONN_ATTRS_TAB, emqx_conn_attrs).
--define(CONN_STATS_TAB, emqx_conn_stats).
+-opaque(attrs() :: #{atom() => term()}).
 
+-opaque(stats() :: #{atom() => integer()}).
+
+%% Tables for channel management.
+-define(CHAN_TAB, emqx_channel).
+
+-define(CONN_TAB, emqx_connection).
+
+-define(SESSION_TAB, emqx_session).
+
+-define(SESSION_P_TAB, emqx_session_p).
+
+%% Chan stats
+-define(CHAN_STATS,
+        [{?CHAN_TAB, 'channels.count', 'channels.max'},
+         {?CONN_TAB, 'connections.count', 'connections.max'},
+         {?SESSION_TAB, 'sessions.count', 'sessions.max'},
+         {?SESSION_P_TAB, 'sessions.persistent.count', 'sessions.persistent.max'}
+        ]).
+
+%% Batch drain
 -define(BATCH_SIZE, 100000).
 
-%% @doc Start the connection manager.
+%% Server name
+-define(CM, ?MODULE).
+
+%% @doc Start the channel manager.
 -spec(start_link() -> startlink_ret()).
 start_link() ->
     gen_server:start_link({local, ?CM}, ?MODULE, [], []).
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% API
-%%------------------------------------------------------------------------------
-
-%% @doc Register a connection.
--spec(register_connection(emqx_types:client_id()) -> ok).
-register_connection(ClientId) when is_binary(ClientId) ->
-    register_connection(ClientId, self()).
-
--spec(register_connection(emqx_types:client_id(), pid()) -> ok).
-register_connection(ClientId, ConnPid) when is_binary(ClientId), is_pid(ConnPid) ->
-    true = ets:insert(?CONN_TAB, {ClientId, ConnPid}),
-    notify({registered, ClientId, ConnPid}).
-
-%% @doc Unregister a connection.
--spec(unregister_connection(emqx_types:client_id()) -> ok).
-unregister_connection(ClientId) when is_binary(ClientId) ->
-    unregister_connection(ClientId, self()).
-
--spec(unregister_connection(emqx_types:client_id(), pid()) -> ok).
-unregister_connection(ClientId, ConnPid) when is_binary(ClientId), is_pid(ConnPid) ->
-    true = do_unregister_connection({ClientId, ConnPid}),
-    notify({unregistered, ConnPid}).
-
-do_unregister_connection(Conn) ->
-    true = ets:delete(?CONN_STATS_TAB, Conn),
-    true = ets:delete(?CONN_ATTRS_TAB, Conn),
-    true = ets:delete_object(?CONN_TAB, Conn).
-
-%% @doc Get conn attrs
--spec(get_conn_attrs(emqx_types:client_id()) -> list()).
-get_conn_attrs(ClientId) when is_binary(ClientId) ->
-    ConnPid = lookup_conn_pid(ClientId),
-    get_conn_attrs(ClientId, ConnPid).
-
--spec(get_conn_attrs(emqx_types:client_id(), pid()) -> list()).
-get_conn_attrs(ClientId, ConnPid) when is_binary(ClientId) ->
-    emqx_tables:lookup_value(?CONN_ATTRS_TAB, {ClientId, ConnPid}, []).
-
-%% @doc Set conn attrs
--spec(set_conn_attrs(emqx_types:client_id(), list()) -> true).
-set_conn_attrs(ClientId, Attrs) when is_binary(ClientId) ->
-    set_conn_attrs(ClientId, self(), Attrs).
-
--spec(set_conn_attrs(emqx_types:client_id(), pid(), list()) -> true).
-set_conn_attrs(ClientId, ConnPid, Attrs) when is_binary(ClientId), is_pid(ConnPid) ->
-    Conn = {ClientId, ConnPid},
-    ets:insert(?CONN_ATTRS_TAB, {Conn, Attrs}).
-
-%% @doc Get conn stats
--spec(get_conn_stats(emqx_types:client_id()) -> list(emqx_stats:stats())).
-get_conn_stats(ClientId) when is_binary(ClientId) ->
-    ConnPid = lookup_conn_pid(ClientId),
-    get_conn_stats(ClientId, ConnPid).
-
--spec(get_conn_stats(emqx_types:client_id(), pid()) -> list(emqx_stats:stats())).
-get_conn_stats(ClientId, ConnPid) when is_binary(ClientId) ->
-    Conn = {ClientId, ConnPid},
-    emqx_tables:lookup_value(?CONN_STATS_TAB, Conn, []).
+%%--------------------------------------------------------------------
+
+%% @doc Register a channel.
+-spec(register_channel(emqx_types:client_id()) -> ok).
+register_channel(ClientId) when is_binary(ClientId) ->
+    register_channel(ClientId, self()).
+
+-spec(register_channel(emqx_types:client_id(), chan_pid()) -> ok).
+register_channel(ClientId, ChanPid) ->
+    Chan = {ClientId, ChanPid},
+    true = ets:insert(?CHAN_TAB, Chan),
+    ok = emqx_cm_registry:register_channel(Chan),
+    cast({registered, Chan}).
+
+%% @doc Unregister a channel.
+-spec(unregister_channel(emqx_types:client_id()) -> ok).
+unregister_channel(ClientId) when is_binary(ClientId) ->
+    unregister_channel(ClientId, self()).
+
+-spec(unregister_channel(emqx_types:client_id(), chan_pid()) -> ok).
+unregister_channel(ClientId, ChanPid) ->
+    Chan = {ClientId, ChanPid},
+    true = do_unregister_channel(Chan),
+    cast({unregistered, Chan}).
+
+%% @private
+do_unregister_channel(Chan) ->
+    ok = emqx_cm_registry:unregister_channel(Chan),
+    true = ets:delete_object(?SESSION_P_TAB, Chan),
+    true = ets:delete(?SESSION_TAB, Chan),
+    true = ets:delete(?CONN_TAB, Chan),
+    ets:delete_object(?CHAN_TAB, Chan).
+
+%% @doc Get conn attrs.
+-spec(get_conn_attrs(emqx_types:client_id()) -> maybe(attrs())).
+get_conn_attrs(ClientId) ->
+    with_channel(ClientId, fun(ChanPid) ->
+                                   get_conn_attrs(ClientId, ChanPid)
+                           end).
+
+-spec(get_conn_attrs(emqx_types:client_id(), chan_pid()) -> maybe(attrs())).
+get_conn_attrs(ClientId, ChanPid) when node(ChanPid) == node() ->
+    Chan = {ClientId, ChanPid},
+    try ets:lookup_element(?CONN_TAB, Chan, 2) of
+        Attrs -> Attrs
+    catch
+        error:badarg -> undefined
+    end;
+get_conn_attrs(ClientId, ChanPid) ->
+    rpc_call(node(ChanPid), get_conn_attrs, [ClientId, ChanPid]).
+
+%% @doc Set conn attrs.
+-spec(set_conn_attrs(emqx_types:client_id(), attrs()) -> ok).
+set_conn_attrs(ClientId, Attrs) when is_binary(ClientId), is_map(Attrs) ->
+    Chan = {ClientId, self()},
+    case ets:update_element(?CONN_TAB, Chan, {2, Attrs}) of
+        true  -> ok;
+        false -> true = ets:insert(?CONN_TAB, {Chan, Attrs, #{}}),
+                 ok
+    end.
+
+%% @doc Get conn stats.
+-spec(get_conn_stats(emqx_types:client_id()) -> maybe(stats())).
+get_conn_stats(ClientId) ->
+    with_channel(ClientId, fun(ChanPid) ->
+                                   get_conn_stats(ClientId, ChanPid)
+                           end).
+
+-spec(get_conn_stats(emqx_types:client_id(), chan_pid()) -> maybe(stats())).
+get_conn_stats(ClientId, ChanPid) when node(ChanPid) == node() ->
+    Chan = {ClientId, ChanPid},
+    try ets:lookup_element(?CONN_TAB, Chan, 3) of
+        Stats -> Stats
+    catch
+        error:badarg -> undefined
+    end;
+get_conn_stats(ClientId, ChanPid) ->
+    rpc_call(node(ChanPid), get_conn_stats, [ClientId, ChanPid]).
 
 %% @doc Set conn stats.
--spec(set_conn_stats(emqx_types:client_id(), list(emqx_stats:stats())) -> true).
+-spec(set_conn_stats(emqx_types:client_id(), stats()) -> ok).
 set_conn_stats(ClientId, Stats) when is_binary(ClientId) ->
     set_conn_stats(ClientId, self(), Stats).
 
--spec(set_conn_stats(emqx_types:client_id(), pid(), list(emqx_stats:stats())) -> true).
-set_conn_stats(ClientId, ConnPid, Stats) when is_binary(ClientId), is_pid(ConnPid) ->
-    Conn = {ClientId, ConnPid},
-    ets:insert(?CONN_STATS_TAB, {Conn, Stats}).
+-spec(set_conn_stats(emqx_types:client_id(), chan_pid(), stats()) -> ok).
+set_conn_stats(ClientId, ChanPid, Stats) ->
+    Chan = {ClientId, ChanPid},
+    _ = ets:update_element(?CONN_TAB, Chan, {3, Stats}),
+    ok.
+
+%% @doc Open a session.
+-spec(open_session(map()) -> {ok, emqx_session:session()}
+                           | {error, Reason :: term()}).
+open_session(Attrs = #{clean_start := true,
+                       client_id := ClientId}) ->
+    CleanStart = fun(_) ->
+                     ok = discard_session(ClientId),
+                     {ok, emqx_session:new(Attrs)}
+                 end,
+    emqx_cm_locker:trans(ClientId, CleanStart);
+
+open_session(Attrs = #{clean_start := false,
+                       client_id := ClientId}) ->
+    ResumeStart = fun(_) ->
+                      case resume_session(ClientId) of
+                          {ok, Session} ->
+                              {ok, Session, true};
+                          {error, not_found} ->
+                              {ok, emqx_session:new(Attrs)}
+                      end
+                  end,
+    emqx_cm_locker:trans(ClientId, ResumeStart).
+
+%% @doc Try to resume a session.
+-spec(resume_session(emqx_types:client_id())
+      -> {ok, emqx_session:session()} | {error, Reason :: term()}).
+resume_session(ClientId) ->
+    case lookup_channels(ClientId) of
+        [] -> {error, not_found};
+        [ChanPid] ->
+            emqx_channel:resume(ChanPid);
+        ChanPids ->
+            [ChanPid|StalePids] = lists:reverse(ChanPids),
+            ?LOG(error, "[SM] More than one channel found: ~p", [ChanPids]),
+            lists:foreach(fun(StalePid) ->
+                              catch emqx_channel:discard(StalePid)
+                          end, StalePids),
+            emqx_channel:resume(ChanPid)
+    end.
 
-%% @doc Lookup connection pid.
--spec(lookup_conn_pid(emqx_types:client_id()) -> maybe(pid())).
-lookup_conn_pid(ClientId) when is_binary(ClientId) ->
-    emqx_tables:lookup_value(?CONN_TAB, ClientId).
+%% @doc Discard all the sessions identified by the ClientId.
+-spec(discard_session(emqx_types:client_id()) -> ok).
+discard_session(ClientId) when is_binary(ClientId) ->
+    case lookup_channels(ClientId) of
+        [] -> ok;
+        ChanPids ->
+            lists:foreach(
+              fun(ChanPid) ->
+                      try emqx_channel:discard(ChanPid)
+                      catch
+                          _:Error:_Stk ->
+                              ?LOG(warning, "[SM] Failed to discard ~p: ~p", [ChanPid, Error])
+                      end
+              end, ChanPids)
+    end.
+
+%% @doc Get session attrs.
+-spec(get_session_attrs(emqx_types:client_id()) -> attrs()).
+get_session_attrs(ClientId) ->
+    with_channel(ClientId, fun(ChanPid) ->
+                                   get_session_attrs(ClientId, ChanPid)
+                           end).
+
+-spec(get_session_attrs(emqx_types:client_id(), chan_pid()) -> maybe(attrs())).
+get_session_attrs(ClientId, ChanPid) when node(ChanPid) == node() ->
+    Chan = {ClientId, ChanPid},
+    try ets:lookup_element(?SESSION_TAB, Chan, 2) of
+        Attrs -> Attrs
+    catch
+        error:badarg -> undefined
+    end;
+get_session_attrs(ClientId, ChanPid) ->
+    rpc_call(node(ChanPid), get_session_attrs, [ClientId, ChanPid]).
+
+%% @doc Set session attrs.
+-spec(set_session_attrs(emqx_types:client_id(), attrs()) -> ok).
+set_session_attrs(ClientId, Attrs) when is_binary(ClientId) ->
+    Chan = {ClientId, self()},
+    case ets:update_element(?SESSION_TAB, Chan, {2, Attrs}) of
+        true -> ok;
+        false ->
+            true = ets:insert(?SESSION_TAB, {Chan, Attrs, #{}}),
+            is_clean_start(Attrs) orelse ets:insert(?SESSION_P_TAB, Chan),
+            ok
+    end.
+
+%% @doc Is clean start?
+is_clean_start(#{clean_start := false}) -> false;
+is_clean_start(_Attrs) -> true.
+
+%% @doc Get session stats.
+-spec(get_session_stats(emqx_types:client_id()) -> stats()).
+get_session_stats(ClientId) ->
+    with_channel(ClientId, fun(ChanPid) ->
+                                   get_session_stats(ClientId, ChanPid)
+                           end).
+
+-spec(get_session_stats(emqx_types:client_id(), chan_pid()) -> maybe(stats())).
+get_session_stats(ClientId, ChanPid) when node(ChanPid) == node() ->
+    Chan = {ClientId, ChanPid},
+    try ets:lookup_element(?SESSION_TAB, Chan, 3) of
+        Stats -> Stats
+    catch
+        error:badarg -> undefined
+    end;
+get_session_stats(ClientId, ChanPid) ->
+    rpc_call(node(ChanPid), get_session_stats, [ClientId, ChanPid]).
+
+%% @doc Set session stats.
+-spec(set_session_stats(emqx_types:client_id(), stats()) -> ok).
+set_session_stats(ClientId, Stats) when is_binary(ClientId) ->
+    set_session_stats(ClientId, self(), Stats).
+
+-spec(set_session_stats(emqx_types:client_id(), chan_pid(), stats()) -> ok).
+set_session_stats(ClientId, ChanPid, Stats) ->
+    Chan = {ClientId, ChanPid},
+    _ = ets:update_element(?SESSION_TAB, Chan, {3, Stats}),
+    ok.
+
+with_channel(ClientId, Fun) ->
+    case lookup_channels(ClientId) of
+        []    -> undefined;
+        [Pid] -> Fun(Pid);
+        Pids  -> Fun(lists:last(Pids))
+    end.
 
-notify(Msg) ->
-    gen_server:cast(?CM, {notify, Msg}).
+%% @doc Lookup channels.
+-spec(lookup_channels(emqx_types:client_id()) -> list(chan_pid())).
+lookup_channels(ClientId) ->
+    lookup_channels(global, ClientId).
 
-%%-----------------------------------------------------------------------------
+%% @doc Lookup local or global channels.
+-spec(lookup_channels(local | global, emqx_types:client_id()) -> list(chan_pid())).
+lookup_channels(global, ClientId) ->
+    case emqx_cm_registry:is_enabled() of
+        true ->
+            emqx_cm_registry:lookup_channels(ClientId);
+        false ->
+            lookup_channels(local, ClientId)
+    end;
+
+lookup_channels(local, ClientId) ->
+    [ChanPid || {_, ChanPid} <- ets:lookup(?CHAN_TAB, ClientId)].
+
+%% @private
+rpc_call(Node, Fun, Args) ->
+    case rpc:call(Node, ?MODULE, Fun, Args) of
+        {badrpc, Reason} -> error(Reason);
+        Res -> Res
+    end.
+
+%% @private
+cast(Msg) -> gen_server:cast(?CM, Msg).
+
+%%--------------------------------------------------------------------
 %% gen_server callbacks
-%%-----------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 init([]) ->
-    TabOpts = [public, set, {write_concurrency, true}],
-    ok = emqx_tables:new(?CONN_TAB, [{read_concurrency, true} | TabOpts]),
-    ok = emqx_tables:new(?CONN_ATTRS_TAB, TabOpts),
-    ok = emqx_tables:new(?CONN_STATS_TAB, TabOpts),
-    ok = emqx_stats:update_interval(conn_stats, fun ?MODULE:stats_fun/0),
-    {ok, #{conn_pmon => emqx_pmon:new()}}.
+    TabOpts = [public, {write_concurrency, true}],
+    ok = emqx_tables:new(?CHAN_TAB, [bag, {read_concurrency, true} | TabOpts]),
+    ok = emqx_tables:new(?CONN_TAB, [set, compressed | TabOpts]),
+    ok = emqx_tables:new(?SESSION_TAB, [set, compressed | TabOpts]),
+    ok = emqx_tables:new(?SESSION_P_TAB, [bag | TabOpts]),
+    ok = emqx_stats:update_interval(chan_stats, fun ?MODULE:stats_fun/0),
+    {ok, #{chan_pmon => emqx_pmon:new()}}.
 
 handle_call(Req, _From, State) ->
     ?LOG(error, "Unexpected call: ~p", [Req]),
     {reply, ignored, State}.
 
-handle_cast({notify, {registered, ClientId, ConnPid}}, State = #{conn_pmon := PMon}) ->
-    {noreply, State#{conn_pmon := emqx_pmon:monitor(ConnPid, ClientId, PMon)}};
+handle_cast({registered, {ClientId, ChanPid}}, State = #{chan_pmon := PMon}) ->
+    PMon1 = emqx_pmon:monitor(ChanPid, ClientId, PMon),
+    {noreply, State#{chan_pmon := PMon1}};
 
-handle_cast({notify, {unregistered, ConnPid}}, State = #{conn_pmon := PMon}) ->
-    {noreply, State#{conn_pmon := emqx_pmon:demonitor(ConnPid, PMon)}};
+handle_cast({unregistered, {_ClientId, ChanPid}}, State = #{chan_pmon := PMon}) ->
+    PMon1 = emqx_pmon:demonitor(ChanPid, PMon),
+    {noreply, State#{chan_pmon := PMon1}};
 
 handle_cast(Msg, State) ->
     ?LOG(error, "Unexpected cast: ~p", [Msg]),
     {noreply, State}.
 
-handle_info({'DOWN', _MRef, process, Pid, _Reason}, State = #{conn_pmon := PMon}) ->
-    ConnPids = [Pid | emqx_misc:drain_down(?BATCH_SIZE)],
-    {Items, PMon1} = emqx_pmon:erase_all(ConnPids, PMon),
-    ok = emqx_pool:async_submit(
-           fun lists:foreach/2, [fun clean_down/1, Items]),
-    {noreply, State#{conn_pmon := PMon1}};
+handle_info({'DOWN', _MRef, process, Pid, _Reason}, State = #{chan_pmon := PMon}) ->
+    ChanPids = [Pid | emqx_misc:drain_down(?BATCH_SIZE)],
+    {Items, PMon1} = emqx_pmon:erase_all(ChanPids, PMon),
+    ok = emqx_pool:async_submit(fun lists:foreach/2, [fun clean_down/1, Items]),
+    {noreply, State#{chan_pmon := PMon1}};
 
 handle_info(Info, State) ->
     ?LOG(error, "Unexpected info: ~p", [Info]),
     {noreply, State}.
 
 terminate(_Reason, _State) ->
-    emqx_stats:cancel_update(conn_stats).
+    emqx_stats:cancel_update(chan_stats).
 
 code_change(_OldVsn, State, _Extra) ->
     {ok, State}.
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% Internal functions
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
-clean_down({Pid, ClientId}) ->
-    Conn = {ClientId, Pid},
-    case ets:member(?CONN_TAB, ClientId)
-         orelse ets:member(?CONN_ATTRS_TAB, Conn) of
-        true ->
-            do_unregister_connection(Conn);
-        false -> false
-    end.
+clean_down({ChanPid, ClientId}) ->
+    Chan = {ClientId, ChanPid},
+    do_unregister_channel(Chan).
 
 stats_fun() ->
-    case ets:info(?CONN_TAB, size) of
+    lists:foreach(fun update_stats/1, ?CHAN_STATS).
+
+update_stats({Tab, Stat, MaxStat}) ->
+    case ets:info(Tab, size) of
         undefined -> ok;
-        Size -> emqx_stats:setstat('connections.count', 'connections.max', Size)
+        Size -> emqx_stats:setstat(Stat, MaxStat, Size)
     end.
+

+ 4 - 6
src/emqx_sm_locker.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,8 +12,9 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
--module(emqx_sm_locker).
+-module(emqx_cm_locker).
 
 -include("emqx.hrl").
 -include("types.hrl").
@@ -26,10 +28,6 @@
         , unlock/1
         ]).
 
-%%------------------------------------------------------------------------------
-%% APIs
-%%------------------------------------------------------------------------------
-
 -spec(start_link() -> startlink_ret()).
 start_link() ->
     ekka_locker:start_link(?MODULE).

+ 55 - 40
src/emqx_sm_registry.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,8 +12,10 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
--module(emqx_sm_registry).
+%% Global Channel Registry
+-module(emqx_cm_registry).
 
 -behaviour(gen_server).
 
@@ -24,12 +27,14 @@
 
 -export([start_link/0]).
 
--export([ is_enabled/0
-        , register_session/1
-        , lookup_session/1
-        , unregister_session/1
+-export([is_enabled/0]).
+
+-export([ register_channel/1
+        , unregister_channel/1
         ]).
 
+-export([lookup_channels/1]).
+
 %% gen_server callbacks
 -export([ init/1
         , handle_call/3
@@ -40,57 +45,67 @@
         ]).
 
 -define(REGISTRY, ?MODULE).
--define(TAB, emqx_session_registry).
--define(LOCK, {?MODULE, cleanup_sessions}).
-
--record(global_session, {sid, pid}).
+-define(TAB, emqx_channel_registry).
+-define(LOCK, {?MODULE, cleanup_down}).
 
--type(session_pid() :: pid()).
-
-%%------------------------------------------------------------------------------
-%% APIs
-%%------------------------------------------------------------------------------
+-record(channel, {chid, pid}).
 
-%% @doc Start the global session manager.
+%% @doc Start the global channel registry.
 -spec(start_link() -> startlink_ret()).
 start_link() ->
     gen_server:start_link({local, ?REGISTRY}, ?MODULE, [], []).
 
+%%--------------------------------------------------------------------
+%% APIs
+%%--------------------------------------------------------------------
+
+%% @doc Is the global registry enabled?
 -spec(is_enabled() -> boolean()).
 is_enabled() ->
-    emqx_config:get_env(enable_session_registry, true).
+    emqx_config:get_env(enable_channel_registry, true).
 
--spec(lookup_session(emqx_types:client_id()) -> list(session_pid())).
-lookup_session(ClientId) ->
-    [SessPid || #global_session{pid = SessPid} <- mnesia:dirty_read(?TAB, ClientId)].
+%% @doc Register a global channel.
+-spec(register_channel(emqx_types:client_id()
+                    | {emqx_types:client_id(), pid()}) -> ok).
+register_channel(ClientId) when is_binary(ClientId) ->
+    register_channel({ClientId, self()});
 
--spec(register_session({emqx_types:client_id(), session_pid()}) -> ok).
-register_session({ClientId, SessPid}) when is_binary(ClientId), is_pid(SessPid) ->
+register_channel({ClientId, ChanPid}) when is_binary(ClientId), is_pid(ChanPid) ->
     case is_enabled() of
-        true -> mnesia:dirty_write(?TAB, record(ClientId, SessPid));
+        true -> mnesia:dirty_write(?TAB, record(ClientId, ChanPid));
         false -> ok
     end.
 
--spec(unregister_session({emqx_types:client_id(), session_pid()}) -> ok).
-unregister_session({ClientId, SessPid}) when is_binary(ClientId), is_pid(SessPid) ->
+%% @doc Unregister a global channel.
+-spec(unregister_channel(emqx_types:client_id()
+                      | {emqx_types:client_id(), pid()}) -> ok).
+unregister_channel(ClientId) when is_binary(ClientId) ->
+    unregister_channel({ClientId, self()});
+
+unregister_channel({ClientId, ChanPid}) when is_binary(ClientId), is_pid(ChanPid) ->
     case is_enabled() of
-        true -> mnesia:dirty_delete_object(?TAB, record(ClientId, SessPid));
+        true -> mnesia:dirty_delete_object(?TAB, record(ClientId, ChanPid));
         false -> ok
     end.
 
-record(ClientId, SessPid) ->
-    #global_session{sid = ClientId, pid = SessPid}.
+%% @doc Lookup the global channels.
+-spec(lookup_channels(emqx_types:client_id()) -> list(pid())).
+lookup_channels(ClientId) ->
+    [ChanPid || #channel{pid = ChanPid} <- mnesia:dirty_read(?TAB, ClientId)].
+
+record(ClientId, ChanPid) ->
+    #channel{chid = ClientId, pid = ChanPid}.
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% gen_server callbacks
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 init([]) ->
     ok = ekka_mnesia:create_table(?TAB, [
                 {type, bag},
                 {ram_copies, [node()]},
-                {record_name, global_session},
-                {attributes, record_info(fields, global_session)},
+                {record_name, channel},
+                {attributes, record_info(fields, channel)},
                 {storage_properties, [{ets, [{read_concurrency, true},
                                              {write_concurrency, true}]}]}]),
     ok = ekka_mnesia:copy_table(?TAB),
@@ -108,7 +123,7 @@ handle_cast(Msg, State) ->
 handle_info({membership, {mnesia, down, Node}}, State) ->
     global:trans({?LOCK, self()},
                  fun() ->
-                     mnesia:transaction(fun cleanup_sessions/1, [Node])
+                     mnesia:transaction(fun cleanup_channels/1, [Node])
                  end),
     {noreply, State};
 
@@ -125,14 +140,14 @@ terminate(_Reason, _State) ->
 code_change(_OldVsn, State, _Extra) ->
     {ok, State}.
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% Internal functions
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
-cleanup_sessions(Node) ->
-    Pat = [{#global_session{pid = '$1', _ = '_'}, [{'==', {node, '$1'}, Node}], ['$_']}],
-    lists:foreach(fun delete_session/1, mnesia:select(?TAB, Pat, write)).
+cleanup_channels(Node) ->
+    Pat = [{#channel{pid = '$1', _ = '_'}, [{'==', {node, '$1'}, Node}], ['$_']}],
+    lists:foreach(fun delete_channel/1, mnesia:select(?TAB, Pat, write)).
 
-delete_session(Session) ->
-    mnesia:delete_object(?TAB, Session, write).
+delete_channel(Chan) ->
+    mnesia:delete_object(?TAB, Chan, write).
 

+ 27 - 5
src/emqx_cm_sup.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_cm_sup).
 
@@ -36,13 +38,33 @@ init([]) ->
                  shutdown => 1000,
                  type => worker,
                  modules => [emqx_flapping]},
+    %% Channel locker
+    Locker = #{id => locker,
+               start => {emqx_cm_locker, start_link, []},
+               restart => permanent,
+               shutdown => 5000,
+               type => worker,
+               modules => [emqx_cm_locker]
+              },
+    %% Channel registry
+    Registry = #{id => registry,
+                 start => {emqx_cm_registry, start_link, []},
+                 restart => permanent,
+                 shutdown => 5000,
+                 type => worker,
+                 modules => [emqx_cm_registry]
+                },
+    %% Channel Manager
     Manager = #{id => manager,
                 start => {emqx_cm, start_link, []},
                 restart => permanent,
-                shutdown => 2000,
+                shutdown => 5000,
                 type => worker,
-                modules => [emqx_cm]},
+                modules => [emqx_cm]
+               },
     SupFlags = #{strategy => one_for_one,
                  intensity => 100,
-                 period => 10},
-    {ok, {SupFlags, [Banned, Manager, Flapping]}}.
+                 period => 10
+                },
+    {ok, {SupFlags, [Banned, Flapping, Locker, Registry, Manager]}}.
+

+ 3 - 1
src/emqx_config.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 %% @doc Hot Configuration
 %%

+ 3 - 1
src/emqx_ctl.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_ctl).
 

+ 16 - 13
src/emqx_flapping.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,9 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
+
+%% @doc This module is used to garbage clean the flapping records.
 
 -module(emqx_flapping).
 
@@ -21,14 +25,12 @@
 
 -export([start_link/0]).
 
-%% This module is used to garbage clean the flapping records
-
 %% gen_statem callbacks
--export([ terminate/3
-        , code_change/4
-        , init/1
+-export([ init/1
         , initialized/3
         , callback_mode/0
+        , terminate/3
+        , code_change/4
         ]).
 
 -define(FLAPPING_TAB, ?MODULE).
@@ -37,15 +39,15 @@
 
 -export([check/3]).
 
--record(flapping,
-        { client_id   :: binary()
-        , check_count :: integer()
-        , timestamp   :: integer()
-        }).
+-record(flapping, {
+          client_id :: binary(),
+          check_count :: integer(),
+          timestamp :: integer()
+         }).
 
 -type(flapping_record() :: #flapping{}).
--type(flapping_state() :: flapping | ok).
 
+-type(flapping_state() :: flapping | ok).
 
 %% @doc This function is used to initialize flapping records
 %% the expiry time unit is minutes.
@@ -110,7 +112,7 @@ init([]) ->
               , {write_concurrency, true}
               , {read_concurrency, true}],
     ok = emqx_tables:new(?FLAPPING_TAB, TabOpts),
-    {ok, initialized, #{timer_interval => TimerInterval}}.
+    {ok, initialized, #{timer_interval => Interval}}.
 
 callback_mode() -> [state_functions, state_enter].
 
@@ -137,3 +139,4 @@ clean_expired_records() ->
     NowTime = emqx_time:now_secs(),
     MatchSpec = [{{'$1', '$2', '$3'},[{'<', '$3', NowTime}], [true]}],
     ets:select_delete(?FLAPPING_TAB, MatchSpec).
+

+ 13 - 9
src/emqx_frame.erl

@@ -21,6 +21,7 @@
 
 -export([ initial_parse_state/0
         , initial_parse_state/1
+        , init_serializer/1
         ]).
 
 -export([ parse/1
@@ -29,22 +30,22 @@
         , serialize/2
         ]).
 
+-export_type([ options/0
+             , parse_state/0
+             , parse_result/0
+             ]).
+
 -type(options() :: #{max_size => 1..?MAX_PACKET_SIZE,
-                     version  => emqx_mqtt_types:version()
+                     version  => emqx_mqtt:version()
                     }).
 
 -opaque(parse_state() :: {none, options()} | {more, cont_fun()}).
 
 -opaque(parse_result() :: {ok, parse_state()}
-                        | {ok, emqx_mqtt_types:packet(), binary(), parse_state()}).
+                        | {ok, emqx_mqtt:packet(), binary(), parse_state()}).
 
 -type(cont_fun() :: fun((binary()) -> parse_result())).
 
--export_type([ options/0
-             , parse_state/0
-             , parse_result/0
-             ]).
-
 -define(none(Opts), {none, Opts}).
 -define(more(Cont), {more, Cont}).
 -define(DEFAULT_OPTIONS,
@@ -385,11 +386,14 @@ parse_binary_data(<<Len:16/big, Data:Len/binary, Rest/binary>>) ->
 %% Serialize MQTT Packet
 %%--------------------------------------------------------------------
 
--spec(serialize(emqx_mqtt_types:packet()) -> iodata()).
+init_serializer(Options) ->
+    fun(Packet) -> serialize(Packet, Options) end.
+
+-spec(serialize(emqx_mqtt:packet()) -> iodata()).
 serialize(Packet) ->
     serialize(Packet, ?DEFAULT_OPTIONS).
 
--spec(serialize(emqx_mqtt_types:packet(), options()) -> iodata()).
+-spec(serialize(emqx_mqtt:packet(), options()) -> iodata()).
 serialize(#mqtt_packet{header   = Header,
                        variable = Variable,
                        payload  = Payload}, Options) when is_map(Options) ->

+ 11 - 7
src/emqx_gc.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,13 +12,16 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
-%% @doc This module manages an opaque collection of statistics data used to
-%% force garbage collection on `self()' process when hitting thresholds.
+%%--------------------------------------------------------------------
+%% @doc This module manages an opaque collection of statistics data used
+%% to force garbage collection on `self()' process when hitting thresholds.
 %% Namely:
 %% (1) Total number of messages passed through
 %% (2) Total data volume passed through
 %% @end
+%%--------------------------------------------------------------------
 
 -module(emqx_gc).
 
@@ -29,6 +33,8 @@
         , reset/1
         ]).
 
+-export_type([gc_state/0]).
+
 -type(opts() :: #{count => integer(),
                   bytes => integer()}).
 
@@ -37,8 +43,6 @@
 
 -opaque(gc_state() :: {?MODULE, st()}).
 
--export_type([gc_state/0]).
-
 -define(GCS(St), {?MODULE, St}).
 
 -define(disabled, disabled).
@@ -85,9 +89,9 @@ reset(?GCS(St)) ->
 reset(undefined) ->
     undefined.
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% Internal functions
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 -spec(dec(cnt | oct, pos_integer(), st()) -> {boolean(), st()}).
 dec(Key, Num, St) ->

+ 3 - 1
src/emqx_gen_mod.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_gen_mod).
 

+ 3 - 1
src/emqx_guid.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 %% @doc Generate global unique id for mqtt message.
 %%

+ 27 - 15
src/emqx_hooks.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_hooks).
 
@@ -21,7 +23,9 @@
 
 -logger_header("[Hooks]").
 
--export([start_link/0, stop/0]).
+-export([ start_link/0
+        , stop/0
+        ]).
 
 %% Hooks API
 -export([ add/2
@@ -42,6 +46,11 @@
         , code_change/3
         ]).
 
+-export_type([ hookpoint/0
+             , action/0
+             , filter/0
+             ]).
+
 %% Multiple callbacks can be registered on a hookpoint.
 %% The execution order depends on the priority value:
 %%   - Callbacks with greater priority values will be run before
@@ -54,28 +63,32 @@
 -type(action() :: function() | mfa()).
 -type(filter() :: function() | mfa()).
 
--record(callback, {action   :: action(),
-                   filter   :: filter(),
-                   priority :: integer()}).
+-record(callback, {
+          action :: action(),
+          filter :: filter(),
+          priority :: integer()
+         }).
 
--record(hook, {name :: hookpoint(), callbacks :: list(#callback{})}).
-
--export_type([hookpoint/0, action/0, filter/0]).
+-record(hook, {
+          name :: hookpoint(),
+          callbacks :: list(#callback{})
+         }).
 
 -define(TAB, ?MODULE).
 -define(SERVER, ?MODULE).
 
 -spec(start_link() -> startlink_ret()).
 start_link() ->
-    gen_server:start_link({local, ?SERVER}, ?MODULE, [], [{hibernate_after, 1000}]).
+    gen_server:start_link({local, ?SERVER},
+                          ?MODULE, [], [{hibernate_after, 1000}]).
 
 -spec(stop() -> ok).
 stop() ->
     gen_server:stop(?SERVER, normal, infinity).
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% Hooks API
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 %% @doc Register a callback
 -spec(add(hookpoint(), action() | #callback{}) -> ok_or_error(already_exists)).
@@ -113,7 +126,6 @@ run(HookPoint, Args) ->
 run_fold(HookPoint, Args, Acc) ->
     do_run_fold(lookup(HookPoint), Args, Acc).
 
-
 do_run([#callback{action = Action, filter = Filter} | Callbacks], Args) ->
     case filter_passed(Filter, Args) andalso execute(Action, Args) of
         %% stop the hook chain and return
@@ -165,12 +177,12 @@ lookup(HookPoint) ->
         [] -> []
     end.
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% gen_server callbacks
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 init([]) ->
-    ok = emqx_tables:new(?TAB, [{keypos, #hook.name}, {read_concurrency, true}, protected]),
+    ok = emqx_tables:new(?TAB, [{keypos, #hook.name}, {read_concurrency, true}]),
     {ok, #{}}.
 
 handle_call({add, HookPoint, Callback = #callback{action = Action}}, _From, State) ->

+ 9 - 6
src/emqx_inflight.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_inflight).
 
@@ -31,6 +33,8 @@
         , window/1
         ]).
 
+-export_type([inflight/0]).
+
 -type(key() :: term()).
 
 -type(max_size() :: pos_integer()).
@@ -38,17 +42,16 @@
 -opaque(inflight() :: {?MODULE, max_size(), gb_trees:tree()}).
 
 -define(Inflight(Tree), {?MODULE, _MaxSize, Tree}).
--define(Inflight(MaxSize, Tree), {?MODULE, MaxSize, (Tree)}).
 
--export_type([inflight/0]).
+-define(Inflight(MaxSize, Tree), {?MODULE, MaxSize, (Tree)}).
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% APIs
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 -spec(new(non_neg_integer()) -> inflight()).
 new(MaxSize) when MaxSize >= 0 ->
-    {?MODULE, MaxSize, gb_trees:empty()}.
+    ?Inflight(MaxSize, gb_trees:empty()).
 
 -spec(contain(key(), inflight()) -> boolean()).
 contain(Key, ?Inflight(Tree)) ->

+ 7 - 5
src/emqx_json.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_json).
 
@@ -35,12 +37,12 @@ encode(Term, Opts) ->
     jsx:encode(Term, Opts).
 
 -spec(safe_encode(jsx:json_term())
-      -> {ok, jsx:json_text()} | {error, term()}).
+      -> {ok, jsx:json_text()} | {error, Reason :: term()}).
 safe_encode(Term) ->
     safe_encode(Term, []).
 
 -spec(safe_encode(jsx:json_term(), jsx_to_json:config())
-      -> {ok, jsx:json_text()} | {error, term()}).
+      -> {ok, jsx:json_text()} | {error, Reason :: term()}).
 safe_encode(Term, Opts) ->
     try encode(Term, Opts) of
         Json -> {ok, Json}
@@ -58,12 +60,12 @@ decode(Json, Opts) ->
     jsx:decode(Json, Opts).
 
 -spec(safe_decode(jsx:json_text())
-      -> {ok, jsx:json_term()} | {error, term()}).
+      -> {ok, jsx:json_term()} | {error, Reason :: term()}).
 safe_decode(Json) ->
     safe_decode(Json, []).
 
 -spec(safe_decode(jsx:json_text(), jsx_to_json:config())
-      -> {ok, jsx:json_term()} | {error, term()}).
+      -> {ok, jsx:json_term()} | {error, Reason :: term()}).
 safe_decode(Json, Opts) ->
     try decode(Json, Opts) of
         Term -> {ok, Term}

+ 16 - 6
src/emqx_keepalive.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_keepalive).
 
@@ -20,15 +22,22 @@
         , cancel/1
         ]).
 
--record(keepalive, {statfun, statval, tsec, tmsg, tref, repeat = 0}).
+-export_type([keepalive/0]).
 
--opaque(keepalive() :: #keepalive{}).
+-record(keepalive, {
+          statfun,
+          statval,
+          tsec,
+          tmsg,
+          tref,
+          repeat = 0
+         }).
 
--export_type([keepalive/0]).
+-opaque(keepalive() :: #keepalive{}).
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% APIs
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 %% @doc Start a keepalive
 -spec(start(fun(), integer(), any()) -> {ok, keepalive()} | {error, term()}).
@@ -79,3 +88,4 @@ cancel(_) ->
 
 timer(Secs, Msg) ->
     erlang:send_after(timer:seconds(Secs), self(), Msg).
+

+ 3 - 1
src/emqx_kernel_sup.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_kernel_sup).
 

+ 6 - 3
src/emqx_listeners.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 %% @doc Start/Stop MQTT listeners.
 -module(emqx_listeners).
@@ -33,9 +35,9 @@
 
 -type(listener() :: {esockd:proto(), esockd:listen_on(), [esockd:option()]}).
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% APIs
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 %% @doc Start all listeners.
 -spec(start() -> ok).
@@ -167,3 +169,4 @@ format({Addr, Port}) when is_list(Addr) ->
     io_lib:format("~s:~w", [Addr, Port]);
 format({Addr, Port}) when is_tuple(Addr) ->
     io_lib:format("~s:~w", [esockd_net:ntoab(Addr), Port]).
+

+ 8 - 6
src/emqx_logger.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,10 +12,11 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_logger).
 
--compile({no_auto_import,[error/1]}).
+-compile({no_auto_import, [error/1]}).
 
 %% Logs
 -export([ debug/1
@@ -50,9 +52,9 @@
 
 -export([parse_transform/2]).
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% APIs
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 debug(Msg) ->
     logger:debug(Msg).
@@ -125,9 +127,9 @@ set_log_level(Level) ->
 parse_transform(AST, _Opts) ->
     trans(AST, "", []).
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% Internal Functions
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 log_hanlder_info(#{id := Id, level := Level, module := logger_std_h,
                    config := #{type := Type}}) when Type =:= standard_io;

+ 16 - 12
src/emqx_logger_formatter.erl

@@ -34,18 +34,22 @@
 -define(IS_STRING(String),
         (is_list(String) orelse is_binary(String))).
 
-%%%-----------------------------------------------------------------
-%%% Types
--type config() :: #{chars_limit     => pos_integer() | unlimited,
-                    depth           => pos_integer() | unlimited,
-                    max_size        => pos_integer() | unlimited,
-                    report_cb       => logger:report_cb(),
-                    quit        => template()}.
--type template() :: [metakey() | {metakey(),template(),template()} | string()].
--type metakey() :: atom() | [atom()].
-
-%%%-----------------------------------------------------------------
-%%% API
+%%--------------------------------------------------------------------
+%% Types
+
+-type(config() :: #{chars_limit => pos_integer() | unlimited,
+                    depth       => pos_integer() | unlimited,
+                    max_size    => pos_integer() | unlimited,
+                    report_cb   => logger:report_cb(),
+                    quit        => template()}).
+
+-type(template() :: [metakey() | {metakey(),template(),template()} | string()]).
+
+-type(metakey() :: atom() | [atom()]).
+
+%%--------------------------------------------------------------------
+%% API
+
 -spec format(LogEvent,Config) -> unicode:chardata() when
       LogEvent :: logger:log_event(),
       Config :: config().

+ 6 - 3
src/emqx_logger_handler.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_logger_handler).
 
@@ -19,8 +21,8 @@
 -export([init/0]).
 
 init() ->
-    logger:add_handler(emqx_logger_handler, 
-                       emqx_logger_handler, 
+    logger:add_handler(emqx_logger_handler,
+                       emqx_logger_handler,
                        #{level => error,
                          filters => [{easy_filter, {fun filter_by_level/2, []}}],
                          filters_default => stop}).
@@ -41,3 +43,4 @@ filter_by_level(LogEvent = #{level := error}, _Extra) ->
     LogEvent;
 filter_by_level(_LogEvent, _Extra) ->
     stop.
+

+ 3 - 1
src/emqx_message.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_message).
 

+ 13 - 11
src/emqx_metrics.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_metrics).
 
@@ -58,12 +60,12 @@
         , code_change/3
         ]).
 
+-export_type([metric_idx/0]).
+
 -opaque(metric_idx() :: 1..1024).
 
 -type(metric_name() :: atom() | string() | binary()).
 
--export_type([metric_idx/0]).
-
 -define(MAX_SIZE, 1024).
 -define(RESERVED_IDX, 256).
 -define(TAB, ?MODULE).
@@ -149,9 +151,9 @@ start_link() ->
 stop() ->
     gen_server:stop(?SERVER).
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% Metrics API
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 -spec(new(metric_name()) -> ok).
 new(Name) ->
@@ -255,9 +257,9 @@ update_counter(Name, Value) ->
            end,
     counters:add(CRef, CIdx, Value).
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% Inc Received/Sent metrics
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 %% @doc Inc packets received.
 -spec(inc_recv(emqx_mqtt_types:packet()) -> ok).
@@ -343,9 +345,9 @@ do_inc_sent(?PACKET(?AUTH)) ->
 do_inc_sent(_Packet) ->
     ignore.
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% gen_server callbacks
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 init([]) ->
     % Create counters array
@@ -395,9 +397,9 @@ terminate(_Reason, _State) ->
 code_change(_OldVsn, State, _Extra) ->
     {ok, State}.
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% Internal functions
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 reserved_idx('bytes.received')               -> 01;
 reserved_idx('bytes.sent')                   -> 02;

+ 3 - 1
src/emqx_misc.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_misc).
 

+ 9 - 7
src/emqx_mod_acl_internal.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_mod_acl_internal).
 
@@ -37,9 +39,9 @@
 -type(acl_rules() :: #{publish   => [emqx_access_rule:rule()],
                        subscribe => [emqx_access_rule:rule()]}).
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% API
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 load(_Env) ->
     Rules = rules_from_file(acl_file()),
@@ -54,9 +56,9 @@ unload(_Env) ->
 all_rules() ->
     rules_from_file(acl_file()).
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% ACL callbacks
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 %% @doc Check ACL
 -spec(check_acl(emqx_types:credentials(), emqx_types:pubsub(), emqx_topic:topic(),
@@ -73,9 +75,9 @@ check_acl(Credentials, PubSub, Topic, _AclResult, Rules) ->
 reload_acl() ->
     unload([]), load([]).
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% Internal Functions
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 acl_file() ->
     emqx_config:get_env(acl_file).

+ 5 - 3
src/emqx_mod_presence.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_mod_presence).
 
@@ -33,9 +35,9 @@
 
 -define(ATTR_KEYS, [clean_start, proto_ver, proto_name, keepalive]).
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% APIs
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 load(Env) ->
     emqx_hooks:add('client.connected',    fun ?MODULE:on_client_connected/4, [Env]),

+ 3 - 1
src/emqx_mod_rewrite.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_mod_rewrite).
 

+ 3 - 1
src/emqx_mod_subscription.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_mod_subscription).
 

+ 17 - 6
src/emqx_mod_sup.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,11 +12,14 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_mod_sup).
 
 -behaviour(supervisor).
 
+-include("types.hrl").
+
 -export([ start_link/0
         , start_child/1
         , start_child/2
@@ -25,8 +29,14 @@
 -export([init/1]).
 
 %% Helper macro for declaring children of supervisor
--define(CHILD(Mod, Type), {Mod, {Mod, start_link, []}, permanent, 5000, Type, [Mod]}).
-
+-define(CHILD(Mod, Type), #{id => Mod,
+                            start => {Mod, start_link, []},
+                            restart => permanent,
+                            shutdown => 5000,
+                            type => Type,
+                            modules => [Mod]}).
+
+-spec(start_link() -> startlink_ret()).
 start_link() ->
     supervisor:start_link({local, ?MODULE}, ?MODULE, []).
 
@@ -39,13 +49,14 @@ start_child(Mod, Type) when is_atom(Mod) andalso is_atom(Type) ->
 -spec(stop_child(any()) -> ok | {error, term()}).
 stop_child(ChildId) ->
     case supervisor:terminate_child(?MODULE, ChildId) of
-        ok    -> supervisor:delete_child(?MODULE, ChildId);
+        ok -> supervisor:delete_child(?MODULE, ChildId);
         Error -> Error
     end.
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% Supervisor callbacks
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 init([]) ->
     {ok, {{one_for_one, 10, 100}, []}}.
+

+ 17 - 10
src/emqx_modules.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_modules).
 
@@ -22,20 +24,25 @@
         , unload/0
         ]).
 
+%% @doc Load all the extended modules.
 -spec(load() -> ok).
 load() ->
     ok = emqx_mod_acl_internal:load([]),
-    lists:foreach(
-      fun({Mod, Env}) ->
-        ok = Mod:load(Env),
-        ?LOG(info, "Load ~s module successfully.", [Mod])
-      end, emqx_config:get_env(modules, [])).
+    lists:foreach(fun load/1, modules()).
 
+load({Mod, Env}) ->
+    ok = Mod:load(Env),
+    ?LOG(info, "Load ~s module successfully.", [Mod]).
+
+modules() ->
+    emqx_config:get_env(modules, []).
+
+%% @doc Unload all the extended modules.
 -spec(unload() -> ok).
 unload() ->
     ok = emqx_mod_acl_internal:unload([]),
-    lists:foreach(
-      fun({Mod, Env}) ->
-          Mod:unload(Env) end,
-      emqx_config:get_env(modules, [])).
+    lists:foreach(fun unload/1, modules()).
+
+unload({Mod, Env}) ->
+    Mod:unload(Env).
 

+ 7 - 5
src/emqx_mountpoint.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_mountpoint).
 
@@ -25,13 +27,13 @@
 
 -export([replvar/2]).
 
--type(mountpoint() :: binary()).
-
 -export_type([mountpoint/0]).
 
-%%------------------------------------------------------------------------------
+-type(mountpoint() :: binary()).
+
+%%--------------------------------------------------------------------
 %% APIs
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 mount(undefined, Any) ->
     Any;

+ 24 - 7
src/emqx_mqtt_types.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,16 +12,32 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
--module(emqx_mqtt_types).
+%% MQTT Types
+-module(emqx_mqtt).
 
 -include("emqx_mqtt.hrl").
 
--export_type([version/0, qos/0, qos_name/0]).
--export_type([connack/0, reason_code/0]).
--export_type([properties/0, subopts/0]).
+-export_type([ version/0
+             , qos/0
+             , qos_name/0
+             ]).
+
+-export_type([ connack/0
+             , reason_code/0
+             ]).
+
+-export_type([ properties/0
+             , subopts/0
+             ]).
+
 -export_type([topic_filters/0]).
--export_type([packet_id/0, packet_type/0, packet/0]).
+
+-export_type([ packet_id/0
+             , packet_type/0
+             , packet/0
+             ]).
 
 -type(qos() :: ?QOS_0 | ?QOS_1 | ?QOS_2).
 -type(version() :: ?MQTT_PROTO_V3 | ?MQTT_PROTO_V4 | ?MQTT_PROTO_V5).
@@ -38,6 +55,6 @@
                      qos := qos(),
                      rc  => reason_code()
                     }).
--type(topic_filters() :: [{emqx_topic:topic(), subopts()}]).
+-type(topic_filters() :: list({emqx_topic:topic(), subopts()})).
 -type(packet() :: #mqtt_packet{}).
 

+ 3 - 3
src/emqx_mqtt_caps.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 %% @doc MQTTv5 capabilities
 -module(emqx_mqtt_caps).
@@ -35,8 +37,6 @@
                   mqtt_shared_subscription   => boolean(),
                   mqtt_wildcard_subscription => boolean()}).
 
--export_type([caps/0]).
-
 -define(UNLIMITED, 0).
 
 -define(DEFAULT_CAPS, [{max_packet_size,  ?MAX_PACKET_SIZE},

+ 3 - 1
src/emqx_mqtt_props.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 %% @doc MQTT5 Properties
 -module(emqx_mqtt_props).

+ 5 - 1
src/emqx_mqueue.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,7 +12,9 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
+%%--------------------------------------------------------------------
 %% @doc A Simple in-memory message queue.
 %%
 %% Notice that MQTT is not a (on-disk) persistent messaging queue.
@@ -42,6 +45,7 @@
 %%    unless `max_len' is set to `0' which implies (`infinity').
 %%
 %% @end
+%%--------------------------------------------------------------------
 
 -module(emqx_mqueue).
 

+ 57 - 44
src/emqx_os_mon.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_os_mon).
 
@@ -18,19 +20,10 @@
 
 -include("logger.hrl").
 
--logger_header("[OS Monitor]").
+-logger_header("[OS_MON]").
 
 -export([start_link/1]).
 
-%% gen_server callbacks
--export([ init/1
-        , handle_call/3
-        , handle_cast/2
-        , handle_info/2
-        , terminate/2
-        , code_change/3
-        ]).
-
 -export([ get_cpu_check_interval/0
         , set_cpu_check_interval/1
         , get_cpu_high_watermark/0
@@ -45,20 +38,29 @@
         , set_procmem_high_watermark/1
         ]).
 
--define(OS_MON, ?MODULE).
+%% gen_server callbacks
+-export([ init/1
+        , handle_call/3
+        , handle_cast/2
+        , handle_info/2
+        , terminate/2
+        , code_change/3
+        ]).
 
 -define(compat_windows(Expression), case os:type() of
                                         {win32, nt} -> windows;
                                         _Unix -> Expression
                                     end).
 
-%%------------------------------------------------------------------------------
-%% API
-%%------------------------------------------------------------------------------
+-define(OS_MON, ?MODULE).
 
 start_link(Opts) ->
     gen_server:start_link({local, ?OS_MON}, ?MODULE, [Opts], []).
 
+%%--------------------------------------------------------------------
+%% API
+%%--------------------------------------------------------------------
+
 get_cpu_check_interval() ->
     call(get_cpu_check_interval).
 
@@ -95,9 +97,12 @@ get_procmem_high_watermark() ->
 set_procmem_high_watermark(Float) ->
     memsup:set_procmem_high_watermark(Float).
 
-%%------------------------------------------------------------------------------
+call(Req) ->
+    gen_server:call(?OS_MON, Req, infinity).
+
+%%--------------------------------------------------------------------
 %% gen_server callbacks
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 init([Opts]) ->
     _ = ?compat_windows(cpu_sup:util()),
@@ -112,49 +117,59 @@ init([Opts]) ->
 
 handle_call(get_cpu_check_interval, _From, State) ->
     {reply, maps:get(cpu_check_interval, State, undefined), State};
+
 handle_call({set_cpu_check_interval, Seconds}, _From, State) ->
     {reply, ok, State#{cpu_check_interval := Seconds}};
 
 handle_call(get_cpu_high_watermark, _From, State) ->
     {reply, maps:get(cpu_high_watermark, State, undefined), State};
+
 handle_call({set_cpu_high_watermark, Float}, _From, State) ->
     {reply, ok, State#{cpu_high_watermark := Float}};
 
 handle_call(get_cpu_low_watermark, _From, State) ->
     {reply, maps:get(cpu_low_watermark, State, undefined), State};
+
 handle_call({set_cpu_low_watermark, Float}, _From, State) ->
     {reply, ok, State#{cpu_low_watermark := Float}};
 
-handle_call(_Request, _From, State) ->
-    {reply, ok, State}.
+handle_call(Req, _From, State) ->
+    ?LOG(error, "Unexpected call: ~p", [Req]),
+    {reply, ignored, State}.
 
-handle_cast(_Request, State) ->
+handle_cast(Msg, State) ->
+    ?LOG(error, "Unexpected cast: ~p", [Msg]),
     {noreply, State}.
 
 handle_info({timeout, Timer, check}, State = #{timer := Timer,
                                                cpu_high_watermark := CPUHighWatermark,
                                                cpu_low_watermark := CPULowWatermark,
                                                is_cpu_alarm_set := IsCPUAlarmSet}) ->
-    case ?compat_windows(cpu_sup:util()) of
-        0 ->
-            {noreply, State#{timer := undefined}};
-        {error, Reason} ->
-            ?LOG(error, "Failed to get cpu utilization: ~p", [Reason]),
-            {noreply, ensure_check_timer(State)};
-        windows ->
-            {noreply, State};
-        Busy when Busy / 100 >= CPUHighWatermark ->
-            alarm_handler:set_alarm({cpu_high_watermark, Busy}),
-            {noreply, ensure_check_timer(State#{is_cpu_alarm_set := true})};
-        Busy when Busy / 100 < CPULowWatermark ->
-            case IsCPUAlarmSet of
-                true -> alarm_handler:clear_alarm(cpu_high_watermark);
-                false -> ok
-            end,
-            {noreply, ensure_check_timer(State#{is_cpu_alarm_set := false})};
-        _Busy ->
-            {noreply, ensure_check_timer(State)}
-    end.
+    NState = case ?compat_windows(cpu_sup:util()) of
+                 0 ->
+                     State#{timer := undefined};
+                 {error, Reason} ->
+                     ?LOG(error, "Failed to get cpu utilization: ~p", [Reason]),
+                     ensure_check_timer(State);
+                 windows ->
+                     State;
+                 Busy when Busy / 100 >= CPUHighWatermark ->
+                     alarm_handler:set_alarm({cpu_high_watermark, Busy}),
+                     ensure_check_timer(State#{is_cpu_alarm_set := true});
+                 Busy when Busy / 100 < CPULowWatermark ->
+                     case IsCPUAlarmSet of
+                         true  -> alarm_handler:clear_alarm(cpu_high_watermark);
+                         false -> ok
+                     end,
+                     ensure_check_timer(State#{is_cpu_alarm_set := false});
+                 _Busy ->
+                     ensure_check_timer(State)
+             end,
+    {noreply, NState};
+
+handle_info(Info, State) ->
+    ?LOG(error, "Unexpected info: ~p", [Info]),
+    {noreply, State}.
 
 terminate(_Reason, #{timer := Timer}) ->
     emqx_misc:cancel_timer(Timer).
@@ -162,11 +177,9 @@ terminate(_Reason, #{timer := Timer}) ->
 code_change(_OldVsn, State, _Extra) ->
     {ok, State}.
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% Internal functions
-%%------------------------------------------------------------------------------
-call(Req) ->
-    gen_server:call(?OS_MON, Req, infinity).
+%%--------------------------------------------------------------------
 
 ensure_check_timer(State = #{cpu_check_interval := Interval}) ->
     State#{timer := emqx_misc:start_timer(timer:seconds(Interval), check)}.

+ 8 - 4
src/emqx_packet.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_packet).
 
@@ -40,10 +42,11 @@ protocol_name(?MQTT_PROTO_V5) ->
 type_name(Type) when Type > ?RESERVED andalso Type =< ?AUTH ->
     lists:nth(Type, ?TYPE_NAMES).
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% Validate MQTT Packet
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
+-spec(validate(emqx_mqtt_types:packet()) -> true).
 validate(?SUBSCRIBE_PACKET(_PacketId, _Properties, [])) ->
     error(topic_filters_invalid);
 validate(?SUBSCRIBE_PACKET(PacketId, Properties, TopicFilters)) ->
@@ -110,7 +113,8 @@ validate_qos(QoS) when ?QOS_0 =< QoS, QoS =< ?QOS_2 ->
 validate_qos(_) -> error(bad_qos).
 
 %% @doc From message to packet
--spec(from_message(emqx_mqtt_types:packet_id(), emqx_types:message()) -> emqx_mqtt_types:packet()).
+-spec(from_message(emqx_mqtt_types:packet_id(), emqx_types:message())
+      -> emqx_mqtt_types:packet()).
 from_message(PacketId, #message{qos = QoS, flags = Flags, headers = Headers,
                                 topic = Topic, payload = Payload}) ->
     Flags1 = if Flags =:= undefined ->

+ 3 - 1
src/emqx_pd.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 %% @doc The utility functions for erlang process dictionary.
 -module(emqx_pd).

+ 7 - 5
src/emqx_plugins.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_plugins).
 
@@ -30,9 +32,9 @@
         , load_expand_plugin/1
         ]).
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% APIs
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 %% @doc Init plugins' config
 -spec(init() -> ok).
@@ -47,8 +49,8 @@ init() ->
 
 init_config(CfgFile) ->
     {ok, [AppsEnv]} = file:consult(CfgFile),
-    lists:foreach(fun({AppName, Envs}) ->
-                      [application:set_env(AppName, Par, Val) || {Par, Val} <- Envs]
+    lists:foreach(fun({App, Envs}) ->
+                      [application:set_env(App, Par, Val) || {Par, Val} <- Envs]
                   end, AppsEnv).
 
 %% @doc Load all plugins when the broker started.

+ 7 - 4
src/emqx_pmon.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_pmon).
 
@@ -30,12 +32,13 @@
 
 -export([count/1]).
 
--type(pmon() :: {?MODULE, map()}).
 -export_type([pmon/0]).
 
-%%------------------------------------------------------------------------------
+-opaque(pmon() :: {?MODULE, map()}).
+
+%%--------------------------------------------------------------------
 %% APIs
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 -spec(new() -> pmon()).
 new() ->

+ 9 - 7
src/emqx_pool.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_pool).
 
@@ -47,9 +49,9 @@
 
 -type(task() :: fun() | mfa() | {fun(), Args :: list(any())}).
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% APIs
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 %% @doc Start pool.
 -spec(start_link(atom(), pos_integer()) -> startlink_ret()).
@@ -87,9 +89,9 @@ cast(Msg) ->
 worker() ->
     gproc_pool:pick_worker(?POOL).
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% gen_server callbacks
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 init([Pool, Id]) ->
     true = gproc_pool:connect_worker(Pool, {Pool, Id}),
@@ -123,9 +125,9 @@ terminate(_Reason, #{pool := Pool, id := Id}) ->
 code_change(_OldVsn, State, _Extra) ->
     {ok, State}.
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% Internal functions
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 run({M, F, A}) ->
     erlang:apply(M, F, A);

+ 12 - 4
src/emqx_pool_sup.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_pool_sup).
 
@@ -44,7 +46,8 @@ spec(ChildId, Args) ->
 start_link() ->
     start_link(?POOL, random, {?POOL, start_link, []}).
 
--spec(start_link(atom() | tuple(), atom(), mfa()) -> {ok, pid()} | {error, term()}).
+-spec(start_link(atom() | tuple(), atom(), mfa())
+      -> {ok, pid()} | {error, term()}).
 start_link(Pool, Type, MFA) ->
     start_link(Pool, Type, emqx_vm:schedulers(), MFA).
 
@@ -54,11 +57,16 @@ start_link(Pool, Type, Size, MFA) ->
     supervisor:start_link(?MODULE, [Pool, Type, Size, MFA]).
 
 init([Pool, Type, Size, {M, F, Args}]) ->
-    ensure_pool(Pool, Type, [{size, Size}]),
+    ok = ensure_pool(Pool, Type, [{size, Size}]),
     {ok, {{one_for_one, 10, 3600}, [
         begin
             ensure_pool_worker(Pool, {Pool, I}, I),
-            {{M, I}, {M, F, [Pool, I | Args]}, transient, 5000, worker, [M]}
+            #{id => {M, I},
+              start => {M, F, [Pool, I | Args]},
+              restart => transient,
+              shutdown => 5000,
+              type => worker,
+              modules => [M]}
         end || I <- lists:seq(1, Size)]}}.
 
 ensure_pool(Pool, Type, Opts) ->

+ 2 - 2
src/emqx_pqueue.erl

@@ -57,6 +57,8 @@
         , highest/1
         ]).
 
+-export_type([q/0]).
+
 %%----------------------------------------------------------------------------
 
 -type(priority() :: integer() | 'infinity').
@@ -64,8 +66,6 @@
 -type(pqueue() ::  squeue() | {pqueue, [{priority(), squeue()}]}).
 -type(q() :: pqueue()).
 
--export_type([q/0]).
-
 %%----------------------------------------------------------------------------
 
 -spec(new() -> pqueue()).

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 288 - 342
src/emqx_protocol.erl


+ 4 - 2
src/emqx_psk.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_psk).
 
@@ -35,4 +37,4 @@ lookup(psk, ClientPSKID, _UserState) ->
         Except:Error:Stacktrace ->
           ?LOG(error, "Lookup PSK failed, ~p: ~p", [{Except,Error}, Stacktrace]),
           error
-    end.
+    end.

+ 3 - 1
src/emqx_reason_codes.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 %% @doc MQTT5 reason codes
 -module(emqx_reason_codes).

+ 13 - 11
src/emqx_router.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_router).
 
@@ -68,9 +70,9 @@
 
 -define(ROUTE, emqx_route).
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% Mnesia bootstrap
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 mnesia(boot) ->
     ok = ekka_mnesia:create_table(?ROUTE, [
@@ -83,18 +85,18 @@ mnesia(boot) ->
 mnesia(copy) ->
     ok = ekka_mnesia:copy_table(?ROUTE).
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% Start a router
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 -spec(start_link(atom(), pos_integer()) -> startlink_ret()).
 start_link(Pool, Id) ->
     gen_server:start_link({local, emqx_misc:proc_name(?MODULE, Id)},
                           ?MODULE, [Pool, Id], [{hibernate_after, 1000}]).
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% Route APIs
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 -spec(add_route(emqx_topic:topic()) -> ok | {error, term()}).
 add_route(Topic) when is_binary(Topic) ->
@@ -183,9 +185,9 @@ call(Router, Msg) ->
 pick(Topic) ->
     gproc_pool:pick_worker(router_pool, Topic).
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% gen_server callbacks
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 init([Pool, Id]) ->
     true = gproc_pool:connect_worker(Pool, {Pool, Id}),
@@ -217,9 +219,9 @@ terminate(_Reason, #{pool := Pool, id := Id}) ->
 code_change(_OldVsn, State, _Extra) ->
     {ok, State}.
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% Internal functions
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 insert_direct_route(Route) ->
     mnesia:async_dirty(fun mnesia:write/3, [?ROUTE, Route, sticky_write]).

+ 11 - 9
src/emqx_router_helper.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_router_helper).
 
@@ -51,9 +53,9 @@
 -define(ROUTING_NODE, emqx_routing_node).
 -define(LOCK, {?MODULE, cleanup_routes}).
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% Mnesia bootstrap
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 mnesia(boot) ->
     ok = ekka_mnesia:create_table(?ROUTING_NODE, [
@@ -66,9 +68,9 @@ mnesia(boot) ->
 mnesia(copy) ->
     ok = ekka_mnesia:copy_table(?ROUTING_NODE).
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% API
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 %% @doc Starts the router helper
 -spec(start_link() -> startlink_ret()).
@@ -86,9 +88,9 @@ monitor(Node) when is_atom(Node) ->
         false -> mnesia:dirty_write(?ROUTING_NODE, #routing_node{name = Node})
     end.
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% gen_server callbacks
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 init([]) ->
     ok = ekka:monitor(membership),
@@ -154,9 +156,9 @@ terminate(_Reason, _State) ->
 code_change(_OldVsn, State, _Extra) ->
     {ok, State}.
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% Internal functions
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 stats_fun() ->
     case ets:info(?ROUTE, size) of

+ 3 - 1
src/emqx_router_sup.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_router_sup).
 

+ 4 - 2
src/emqx_rpc.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 %% @doc wrap gen_rpc?
 -module(emqx_rpc).
@@ -32,7 +34,7 @@ cast(Node, Mod, Fun, Args) ->
     filter_result(?RPC:cast(Node, Mod, Fun, Args)).
 
 filter_result(Delivery) ->
-    case Delivery of 
+    case Delivery of
         {badrpc, Reason} -> {badrpc, Reason};
         {badtcp, Reason} -> {badrpc, Reason};
         _ -> Delivery

+ 7 - 5
src/emqx_sequence.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_sequence).
 
@@ -21,17 +23,17 @@
         , delete/1
         ]).
 
+-export_type([seqid/0]).
+
 -type(key() :: term()).
 
 -type(name() :: atom()).
 
 -type(seqid() :: non_neg_integer()).
 
--export_type([seqid/0]).
-
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% APIs
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 %% @doc Create a sequence.
 -spec(create(name()) -> ok).

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 382 - 971
src/emqx_session.erl


+ 0 - 267
src/emqx_session_sup.erl

@@ -1,267 +0,0 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%%     http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-
--module(emqx_session_sup).
-
--behaviour(gen_server).
-
--include("logger.hrl").
--include("types.hrl").
-
--logger_header("[Session Supervisor]").
-
--export([start_link/1]).
-
--export([ start_session/1
-        , count_sessions/0
-        ]).
-
-%% gen_server callbacks
--export([ init/1
-        , handle_call/3
-        , handle_cast/2
-        , handle_info/2
-        , terminate/2
-        , code_change/3
-        ]).
-
--type(shutdown() :: brutal_kill | infinity | pos_integer()).
-
--record(state,
-        { sessions :: #{pid() => emqx_types:client_id()}
-        , mfargs :: mfa()
-        , shutdown :: shutdown()
-        , clean_down :: fun()
-        }).
-
--define(SUP, ?MODULE).
--define(BATCH_EXIT, 100000).
-
-%% @doc Start session supervisor.
--spec(start_link(map()) -> startlink_ret()).
-start_link(SessSpec) when is_map(SessSpec) ->
-    gen_server:start_link({local, ?SUP}, ?MODULE, [SessSpec], []).
-
-%%------------------------------------------------------------------------------
-%% API
-%%------------------------------------------------------------------------------
-
-%% @doc Start a session.
--spec(start_session(map()) -> startlink_ret()).
-start_session(SessAttrs) ->
-    gen_server:call(?SUP, {start_session, SessAttrs}, infinity).
-
-%% @doc Count sessions.
--spec(count_sessions() -> non_neg_integer()).
-count_sessions() ->
-    gen_server:call(?SUP, count_sessions, infinity).
-
-%%------------------------------------------------------------------------------
-%% gen_server callbacks
-%%------------------------------------------------------------------------------
-
-init([Spec]) ->
-    process_flag(trap_exit, true),
-    MFA = maps:get(start, Spec),
-    Shutdown = maps:get(shutdown, Spec, brutal_kill),
-    CleanDown = maps:get(clean_down, Spec, undefined),
-    State = #state{sessions = #{},
-                   mfargs = MFA,
-                   shutdown = Shutdown,
-                   clean_down = CleanDown
-                  },
-    {ok, State}.
-
-handle_call({start_session, SessAttrs = #{client_id := ClientId}}, _From,
-            State = #state{sessions = SessMap, mfargs = {M, F, Args}}) ->
-    try erlang:apply(M, F, [SessAttrs | Args]) of
-        {ok, Pid} ->
-            reply({ok, Pid}, State#state{sessions = maps:put(Pid, ClientId, SessMap)});
-        ignore ->
-            reply(ignore, State);
-        {error, Reason} ->
-            reply({error, Reason}, State)
-    catch
-        _:Error:Stk ->
-            ?LOG(error, "Failed to start session ~p: ~p, stacktrace:~n~p",
-                   [ClientId, Error, Stk]),
-            reply({error, Error}, State)
-    end;
-
-handle_call(count_sessions, _From, State = #state{sessions = SessMap}) ->
-    {reply, maps:size(SessMap), State};
-
-handle_call(Req, _From, State) ->
-    ?LOG(error, "Unexpected call: ~p", [Req]),
-    {reply, ignored, State}.
-
-handle_cast(Msg, State) ->
-    ?LOG(error, "Unexpected cast: ~p", [Msg]),
-    {noreply, State}.
-
-handle_info({'EXIT', Pid, _Reason}, State = #state{sessions = SessMap, clean_down = CleanDown}) ->
-    SessPids = [Pid | drain_exit(?BATCH_EXIT, [])],
-    {SessItems, SessMap1} = erase_all(SessPids, SessMap),
-    (CleanDown =:= undefined)
-        orelse emqx_pool:async_submit(
-                 fun lists:foreach/2, [CleanDown, SessItems]),
-    {noreply, State#state{sessions = SessMap1}};
-
-handle_info(Info, State) ->
-    ?LOG(notice, "Unexpected info: ~p", [Info]),
-    {noreply, State}.
-
-terminate(_Reason, State) ->
-    terminate_children(State).
-
-code_change(_OldVsn, State, _Extra) ->
-    {ok, State}.
-
-%%------------------------------------------------------------------------------
-%% Internal functions
-%%------------------------------------------------------------------------------
-
-drain_exit(0, Acc) ->
-    lists:reverse(Acc);
-drain_exit(Cnt, Acc) ->
-    receive
-        {'EXIT', Pid, _Reason} ->
-            drain_exit(Cnt - 1, [Pid|Acc])
-    after 0 ->
-          lists:reverse(Acc)
-    end.
-
-erase_all(Pids, Map) ->
-    lists:foldl(
-      fun(Pid, {Acc, M}) ->
-            case maps:take(Pid, M) of
-                {Val, M1} ->
-                    {[{Val, Pid}|Acc], M1};
-                error ->
-                    {Acc, M}
-            end
-      end, {[], Map}, Pids).
-
-terminate_children(State = #state{sessions = SessMap, shutdown = Shutdown}) ->
-    {Pids, EStack0} = monitor_children(SessMap),
-    Sz = sets:size(Pids),
-    EStack =
-    case Shutdown of
-        brutal_kill ->
-            sets:fold(fun(P, _) -> exit(P, kill) end, ok, Pids),
-            wait_children(Shutdown, Pids, Sz, undefined, EStack0);
-        infinity ->
-            sets:fold(fun(P, _) -> exit(P, shutdown) end, ok, Pids),
-            wait_children(Shutdown, Pids, Sz, undefined, EStack0);
-        Time when is_integer(Time) ->
-            sets:fold(fun(P, _) -> exit(P, shutdown) end, ok, Pids),
-            TRef = erlang:start_timer(Time, self(), kill),
-            wait_children(Shutdown, Pids, Sz, TRef, EStack0)
-    end,
-    %% Unroll stacked errors and report them
-    dict:fold(fun(Reason, Pid, _) ->
-                  report_error(connection_shutdown_error, Reason, Pid, State)
-              end, ok, EStack).
-
-monitor_children(SessMap) ->
-    lists:foldl(
-      fun(Pid, {Pids, EStack}) ->
-          case monitor_child(Pid) of
-              ok ->
-                  {sets:add_element(Pid, Pids), EStack};
-              {error, normal} ->
-                  {Pids, EStack};
-              {error, Reason} ->
-                  {Pids, dict:append(Reason, Pid, EStack)}
-          end
-      end, {sets:new(), dict:new()}, maps:keys(SessMap)).
-
-%% Help function to shutdown/2 switches from link to monitor approach
-monitor_child(Pid) ->
-    %% Do the monitor operation first so that if the child dies
-    %% before the monitoring is done causing a 'DOWN'-message with
-    %% reason noproc, we will get the real reason in the 'EXIT'-message
-    %% unless a naughty child has already done unlink...
-    erlang:monitor(process, Pid),
-    unlink(Pid),
-
-    receive
-	%% If the child dies before the unlik we must empty
-	%% the mail-box of the 'EXIT'-message and the 'DOWN'-message.
-	{'EXIT', Pid, Reason} ->
-	    receive
-		{'DOWN', _, process, Pid, _} ->
-		    {error, Reason}
-	    end
-    after 0 ->
-	    %% If a naughty child did unlink and the child dies before
-	    %% monitor the result will be that shutdown/2 receives a
-	    %% 'DOWN'-message with reason noproc.
-	    %% If the child should die after the unlink there
-	    %% will be a 'DOWN'-message with a correct reason
-	    %% that will be handled in shutdown/2.
-	    ok
-    end.
-
-wait_children(_Shutdown, _Pids, 0, undefined, EStack) ->
-    EStack;
-wait_children(_Shutdown, _Pids, 0, TRef, EStack) ->
-	%% If the timer has expired before its cancellation, we must empty the
-	%% mail-box of the 'timeout'-message.
-    erlang:cancel_timer(TRef),
-    receive
-        {timeout, TRef, kill} ->
-            EStack
-    after 0 ->
-            EStack
-    end;
-
-%%TODO: Copied from supervisor.erl, rewrite it later.
-wait_children(brutal_kill, Pids, Sz, TRef, EStack) ->
-    receive
-        {'DOWN', _MRef, process, Pid, killed} ->
-            wait_children(brutal_kill, sets:del_element(Pid, Pids), Sz-1, TRef, EStack);
-
-        {'DOWN', _MRef, process, Pid, Reason} ->
-            wait_children(brutal_kill, sets:del_element(Pid, Pids),
-                          Sz-1, TRef, dict:append(Reason, Pid, EStack))
-    end;
-
-wait_children(Shutdown, Pids, Sz, TRef, EStack) ->
-    receive
-        {'DOWN', _MRef, process, Pid, shutdown} ->
-            wait_children(Shutdown, sets:del_element(Pid, Pids), Sz-1, TRef, EStack);
-        {'DOWN', _MRef, process, Pid, normal} ->
-            wait_children(Shutdown, sets:del_element(Pid, Pids), Sz-1, TRef, EStack);
-        {'DOWN', _MRef, process, Pid, Reason} ->
-            wait_children(Shutdown, sets:del_element(Pid, Pids), Sz-1,
-                          TRef, dict:append(Reason, Pid, EStack));
-        {timeout, TRef, kill} ->
-            sets:fold(fun(P, _) -> exit(P, kill) end, ok, Pids),
-            wait_children(Shutdown, Pids, Sz-1, undefined, EStack)
-    end.
-
-report_error(Error, Reason, Pid, #state{mfargs = MFA}) ->
-    SupName  = list_to_atom("esockd_connection_sup - " ++ pid_to_list(self())),
-    ErrorMsg = [{supervisor, SupName},
-                {errorContext, Error},
-                {reason, Reason},
-                {offender, [{pid, Pid},
-                            {name, connection},
-                            {mfargs, MFA}]}],
-    error_logger:error_report(supervisor_report, ErrorMsg).
-
-reply(Repy, State) ->
-    {reply, Repy, State}.
-

+ 18 - 15
src/emqx_shared_sub.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_shared_sub).
 
@@ -67,11 +69,12 @@
 -define(no_ack, no_ack).
 
 -record(state, {pmon}).
+
 -record(emqx_shared_subscription, {group, topic, subpid}).
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% Mnesia bootstrap
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 mnesia(boot) ->
     ok = ekka_mnesia:create_table(?TAB, [
@@ -83,9 +86,9 @@ mnesia(boot) ->
 mnesia(copy) ->
     ok = ekka_mnesia:copy_table(?TAB).
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% API
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 -spec(start_link() -> startlink_ret()).
 start_link() ->
@@ -132,7 +135,7 @@ ack_enabled() ->
 
 do_dispatch(SubPid, Topic, Msg, _Type) when SubPid =:= self() ->
     %% Deadlock otherwise
-    _ = erlang:send(SubPid, {dispatch, Topic, Msg}),
+    _ = erlang:send(SubPid, {deliver, Topic, Msg}),
     ok;
 do_dispatch(SubPid, Topic, Msg, Type) ->
     dispatch_per_qos(SubPid, Topic, Msg, Type).
@@ -140,18 +143,18 @@ do_dispatch(SubPid, Topic, Msg, Type) ->
 %% return either 'ok' (when everything is fine) or 'error'
 dispatch_per_qos(SubPid, Topic, #message{qos = ?QOS_0} = Msg, _Type) ->
     %% For QoS 0 message, send it as regular dispatch
-    _ = erlang:send(SubPid, {dispatch, Topic, Msg}),
+    _ = erlang:send(SubPid, {deliver, Topic, Msg}),
     ok;
 dispatch_per_qos(SubPid, Topic, Msg, retry) ->
     %% Retry implies all subscribers nack:ed, send again without ack
-    _ = erlang:send(SubPid, {dispatch, Topic, Msg}),
+    _ = erlang:send(SubPid, {deliver, Topic, Msg}),
     ok;
 dispatch_per_qos(SubPid, Topic, Msg, fresh) ->
     case ack_enabled() of
         true ->
             dispatch_with_ack(SubPid, Topic, Msg);
         false ->
-            _ = erlang:send(SubPid, {dispatch, Topic, Msg}),
+            _ = erlang:send(SubPid, {deliver, Topic, Msg}),
             ok
     end.
 
@@ -159,7 +162,7 @@ dispatch_with_ack(SubPid, Topic, Msg) ->
     %% For QoS 1/2 message, expect an ack
     Ref = erlang:monitor(process, SubPid),
     Sender = self(),
-    _ = erlang:send(SubPid, {dispatch, Topic, with_ack_ref(Msg, {Sender, Ref})}),
+    _ = erlang:send(SubPid, {deliver, Topic, with_ack_ref(Msg, {Sender, Ref})}),
     Timeout = case Msg#message.qos of
                   ?QOS_1 -> timer:seconds(?SHARED_SUB_QOS1_DISPATCH_TIMEOUT_SECONDS);
                   ?QOS_2 -> infinity
@@ -275,12 +278,12 @@ do_pick_subscriber(Group, Topic, round_robin, _ClientId, Count) ->
 subscribers(Group, Topic) ->
     ets:select(?TAB, [{{emqx_shared_subscription, Group, Topic, '$1'}, [], ['$1']}]).
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% gen_server callbacks
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 init([]) ->
-    mnesia:subscribe({table, ?TAB, simple}),
+    {ok, _} = mnesia:subscribe({table, ?TAB, simple}),
     {atomic, PMon} = mnesia:transaction(fun init_monitors/0),
     ok = emqx_tables:new(?SHARED_SUBS, [protected, bag]),
     ok = emqx_tables:new(?ALIVE_SUBS, [protected, set, {read_concurrency, true}]),
@@ -345,9 +348,9 @@ terminate(_Reason, _State) ->
 code_change(_OldVsn, State, _Extra) ->
     {ok, State}.
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% Internal functions
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 %% keep track of alive remote pids
 maybe_insert_alive_tab(Pid) when ?IS_LOCAL_PID(Pid) -> ok;

+ 0 - 297
src/emqx_sm.erl

@@ -1,297 +0,0 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%%     http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-
--module(emqx_sm).
-
--behaviour(gen_server).
-
--include("emqx.hrl").
--include("logger.hrl").
--include("types.hrl").
-
--logger_header("[SM]").
-
-%% APIs
--export([start_link/0]).
-
--export([ open_session/1
-        , close_session/1
-        , resume_session/2
-        , discard_session/1
-        , discard_session/2
-        , register_session/1
-        , register_session/2
-        , unregister_session/1
-        , unregister_session/2
-        ]).
-
--export([ get_session_attrs/1
-        , get_session_attrs/2
-        , set_session_attrs/2
-        , set_session_attrs/3
-        , get_session_stats/1
-        , get_session_stats/2
-        , set_session_stats/2
-        , set_session_stats/3
-        ]).
-
--export([lookup_session_pids/1]).
-
-%% Internal functions for rpc
--export([dispatch/3]).
-
-%% Internal function for stats
--export([stats_fun/0]).
-
-%% Internal function for emqx_session_sup
--export([clean_down/1]).
-
-%% gen_server callbacks
--export([ init/1
-        , handle_call/3
-        , handle_cast/2
-        , handle_info/2
-        , terminate/2
-        , code_change/3
-        ]).
-
--define(SM, ?MODULE).
-
-%% ETS Tables for session management.
--define(SESSION_TAB, emqx_session).
--define(SESSION_P_TAB, emqx_session_p).
--define(SESSION_ATTRS_TAB, emqx_session_attrs).
--define(SESSION_STATS_TAB, emqx_session_stats).
-
--define(BATCH_SIZE, 100000).
-
-%%------------------------------------------------------------------------------
-%% APIs
-%%------------------------------------------------------------------------------
-
--spec(start_link() -> startlink_ret()).
-start_link() ->
-    gen_server:start_link({local, ?SM}, ?MODULE, [], []).
-
-%% @doc Open a session.
--spec(open_session(map()) -> {ok, pid()} | {ok, pid(), boolean()} | {error, term()}).
-open_session(SessAttrs = #{clean_start := true, client_id := ClientId, conn_pid := ConnPid}) ->
-    CleanStart = fun(_) ->
-                     ok = discard_session(ClientId, ConnPid),
-                     emqx_session_sup:start_session(SessAttrs)
-                 end,
-    emqx_sm_locker:trans(ClientId, CleanStart);
-
-open_session(SessAttrs = #{clean_start := false, client_id := ClientId}) ->
-    ResumeStart = fun(_) ->
-                      case resume_session(ClientId, SessAttrs) of
-                          {ok, SessPid} ->
-                              {ok, SessPid, true};
-                          {error, not_found} ->
-                              emqx_session_sup:start_session(SessAttrs)
-                      end
-                  end,
-    emqx_sm_locker:trans(ClientId, ResumeStart).
-
-%% @doc Discard all the sessions identified by the ClientId.
--spec(discard_session(emqx_types:client_id()) -> ok).
-discard_session(ClientId) when is_binary(ClientId) ->
-    discard_session(ClientId, self()).
-
--spec(discard_session(emqx_types:client_id(), pid()) -> ok).
-discard_session(ClientId, ConnPid) when is_binary(ClientId) ->
-    lists:foreach(
-      fun(SessPid) ->
-          try emqx_session:discard(SessPid, ConnPid)
-          catch
-              _:Error:_Stk ->
-                  ?LOG(warning, "Failed to discard ~p: ~p", [SessPid, Error])
-          end
-      end, lookup_session_pids(ClientId)).
-
-%% @doc Try to resume a session.
--spec(resume_session(emqx_types:client_id(), map()) -> {ok, pid()} | {error, term()}).
-resume_session(ClientId, SessAttrs = #{conn_pid := ConnPid}) ->
-    case lookup_session_pids(ClientId) of
-        [] -> {error, not_found};
-        [SessPid] ->
-            ok = emqx_session:resume(SessPid, SessAttrs),
-            {ok, SessPid};
-        SessPids ->
-            [SessPid|StalePids] = lists:reverse(SessPids),
-            ?LOG(error, "More than one session found: ~p", [SessPids]),
-            lists:foreach(fun(StalePid) ->
-                              catch emqx_session:discard(StalePid, ConnPid)
-                          end, StalePids),
-            ok = emqx_session:resume(SessPid, SessAttrs),
-            {ok, SessPid}
-    end.
-
-%% @doc Close a session.
--spec(close_session(emqx_types:client_id() | pid()) -> ok).
-close_session(ClientId) when is_binary(ClientId) ->
-    case lookup_session_pids(ClientId) of
-        [] -> ok;
-        [SessPid] -> close_session(SessPid);
-        SessPids -> lists:foreach(fun close_session/1, SessPids)
-    end;
-
-close_session(SessPid) when is_pid(SessPid) ->
-    emqx_session:close(SessPid).
-
-%% @doc Register a session.
--spec(register_session(emqx_types:client_id()) -> ok).
-register_session(ClientId) when is_binary(ClientId) ->
-    register_session(ClientId, self()).
-
--spec(register_session(emqx_types:client_id(), pid()) -> ok).
-register_session(ClientId, SessPid) when is_binary(ClientId), is_pid(SessPid) ->
-    Session = {ClientId, SessPid},
-    true = ets:insert(?SESSION_TAB, Session),
-    emqx_sm_registry:register_session(Session).
-
-%% @doc Unregister a session
--spec(unregister_session(emqx_types:client_id()) -> ok).
-unregister_session(ClientId) when is_binary(ClientId) ->
-    unregister_session(ClientId, self()).
-
--spec(unregister_session(emqx_types:client_id(), pid()) -> ok).
-unregister_session(ClientId, SessPid) when is_binary(ClientId), is_pid(SessPid) ->
-    Session = {ClientId, SessPid},
-    true = ets:delete(?SESSION_STATS_TAB, Session),
-    true = ets:delete(?SESSION_ATTRS_TAB, Session),
-    true = ets:delete_object(?SESSION_P_TAB, Session),
-    true = ets:delete_object(?SESSION_TAB, Session),
-    emqx_sm_registry:unregister_session(Session).
-
-%% @doc Get session attrs
--spec(get_session_attrs(emqx_types:client_id()) -> list(emqx_session:attr())).
-get_session_attrs(ClientId) when is_binary(ClientId) ->
-    case lookup_session_pids(ClientId) of
-        [] -> [];
-        [SessPid|_] -> get_session_attrs(ClientId, SessPid)
-    end.
-
--spec(get_session_attrs(emqx_types:client_id(), pid()) -> list(emqx_session:attr())).
-get_session_attrs(ClientId, SessPid) when is_binary(ClientId), is_pid(SessPid) ->
-    emqx_tables:lookup_value(?SESSION_ATTRS_TAB, {ClientId, SessPid}, []).
-
-%% @doc Set session attrs
--spec(set_session_attrs(emqx_types:client_id(), list(emqx_session:attr())) -> true).
-set_session_attrs(ClientId, SessAttrs) when is_binary(ClientId) ->
-    set_session_attrs(ClientId, self(), SessAttrs).
-
--spec(set_session_attrs(emqx_types:client_id(), pid(), list(emqx_session:attr())) -> true).
-set_session_attrs(ClientId, SessPid, SessAttrs) when is_binary(ClientId), is_pid(SessPid) ->
-    Session = {ClientId, SessPid},
-    true = ets:insert(?SESSION_ATTRS_TAB, {Session, SessAttrs}),
-    proplists:get_value(clean_start, SessAttrs, true) orelse ets:insert(?SESSION_P_TAB, Session).
-
-%% @doc Get session stats
--spec(get_session_stats(emqx_types:client_id()) -> list(emqx_stats:stats())).
-get_session_stats(ClientId) when is_binary(ClientId) ->
-    case lookup_session_pids(ClientId) of
-        [] -> [];
-        [SessPid|_] ->
-            get_session_stats(ClientId, SessPid)
-    end.
-
--spec(get_session_stats(emqx_types:client_id(), pid()) -> list(emqx_stats:stats())).
-get_session_stats(ClientId, SessPid) when is_binary(ClientId) ->
-    emqx_tables:lookup_value(?SESSION_STATS_TAB, {ClientId, SessPid}, []).
-
-%% @doc Set session stats
--spec(set_session_stats(emqx_types:client_id(), emqx_stats:stats()) -> true).
-set_session_stats(ClientId, Stats) when is_binary(ClientId) ->
-    set_session_stats(ClientId, self(), Stats).
-
--spec(set_session_stats(emqx_types:client_id(), pid(), emqx_stats:stats()) -> true).
-set_session_stats(ClientId, SessPid, Stats) when is_binary(ClientId), is_pid(SessPid) ->
-    ets:insert(?SESSION_STATS_TAB, {{ClientId, SessPid}, Stats}).
-
-%% @doc Lookup session pid.
--spec(lookup_session_pids(emqx_types:client_id()) -> list(pid())).
-lookup_session_pids(ClientId) ->
-    case emqx_sm_registry:is_enabled() of
-        true -> emqx_sm_registry:lookup_session(ClientId);
-        false ->
-            case emqx_tables:lookup_value(?SESSION_TAB, ClientId) of
-                undefined -> [];
-                SessPid when is_pid(SessPid) -> [SessPid]
-            end
-    end.
-
-%% @doc Dispatch a message to the session.
--spec(dispatch(emqx_types:client_id(), emqx_topic:topic(), emqx_types:message()) -> any()).
-dispatch(ClientId, Topic, Msg) ->
-    case lookup_session_pids(ClientId) of
-        [SessPid|_] when is_pid(SessPid) ->
-            SessPid ! {dispatch, Topic, Msg};
-        [] ->
-            emqx_hooks:run('message.dropped', [#{client_id => ClientId}, Msg])
-    end.
-
-%%------------------------------------------------------------------------------
-%% gen_server callbacks
-%%------------------------------------------------------------------------------
-
-init([]) ->
-    TabOpts = [public, set, {write_concurrency, true}],
-    ok = emqx_tables:new(?SESSION_TAB, [{read_concurrency, true} | TabOpts]),
-    ok = emqx_tables:new(?SESSION_P_TAB, TabOpts),
-    ok = emqx_tables:new(?SESSION_ATTRS_TAB, TabOpts),
-    ok = emqx_tables:new(?SESSION_STATS_TAB, TabOpts),
-    ok = emqx_stats:update_interval(sess_stats, fun ?MODULE:stats_fun/0),
-    {ok, #{}}.
-
-handle_call(Req, _From, State) ->
-    ?LOG(error, "Unexpected call: ~p", [Req]),
-    {reply, ignored, State}.
-
-handle_cast(Msg, State) ->
-    ?LOG(error, "Unexpected cast: ~p", [Msg]),
-    {noreply, State}.
-
-handle_info(Info, State) ->
-    ?LOG(error, "Unexpected info: ~p", [Info]),
-    {noreply, State}.
-
-terminate(_Reason, _State) ->
-    emqx_stats:cancel_update(sess_stats).
-
-code_change(_OldVsn, State, _Extra) ->
-    {ok, State}.
-
-%%------------------------------------------------------------------------------
-%% Internal functions
-%%------------------------------------------------------------------------------
-
-clean_down(Session = {ClientId, SessPid}) ->
-    case ets:member(?SESSION_TAB, ClientId)
-         orelse ets:member(?SESSION_ATTRS_TAB, Session) of
-        true ->
-            unregister_session(ClientId, SessPid);
-        false -> ok
-    end.
-
-stats_fun() ->
-    safe_update_stats(?SESSION_TAB, 'sessions.count', 'sessions.max'),
-    safe_update_stats(?SESSION_P_TAB, 'sessions.persistent.count', 'sessions.persistent.max').
-
-safe_update_stats(Tab, Stat, MaxStat) ->
-    case ets:info(Tab, size) of
-        undefined -> ok;
-        Size -> emqx_stats:setstat(Stat, MaxStat, Size)
-    end.
-

+ 0 - 64
src/emqx_sm_sup.erl

@@ -1,64 +0,0 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%%     http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-
--module(emqx_sm_sup).
-
--behaviour(supervisor).
-
--export([start_link/0]).
-
--export([init/1]).
-
-start_link() ->
-    supervisor:start_link({local, ?MODULE}, ?MODULE, []).
-
-init([]) ->
-    %% Session locker
-    Locker = #{id => locker,
-               start => {emqx_sm_locker, start_link, []},
-               restart => permanent,
-               shutdown => 5000,
-               type => worker,
-               modules => [emqx_sm_locker]
-              },
-    %% Session registry
-    Registry = #{id => registry,
-                 start => {emqx_sm_registry, start_link, []},
-                 restart => permanent,
-                 shutdown => 5000,
-                 type => worker,
-                 modules => [emqx_sm_registry]
-                },
-    %% Session Manager
-    Manager = #{id => manager,
-                start => {emqx_sm, start_link, []},
-                restart => permanent,
-                shutdown => 5000,
-                type => worker,
-                modules => [emqx_sm]
-               },
-    %% Session Sup
-    SessSpec = #{start => {emqx_session, start_link, []},
-                 shutdown => brutal_kill,
-                 clean_down => fun emqx_sm:clean_down/1
-                },
-    SessionSup = #{id => session_sup,
-                   start => {emqx_session_sup, start_link, [SessSpec ]},
-                   restart => transient,
-                   shutdown => infinity,
-                   type => supervisor,
-                   modules => [emqx_session_sup]
-                  },
-    {ok, {{rest_for_one, 10, 3600}, [Locker, Registry, Manager, SessionSup]}}.
-

+ 17 - 9
src/emqx_stats.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_stats).
 
@@ -49,14 +51,18 @@
         , code_change/3
         ]).
 
+-export_type([stats/0]).
+
 -record(update, {name, countdown, interval, func}).
 
--record(state, {timer, updates :: [#update{}], tick_ms :: timeout()}).
+-record(state, {
+          timer :: reference(),
+          updates :: [#update{}],
+          tick_ms :: timeout()
+         }).
 
 -type(stats() :: list({atom(), non_neg_integer()})).
 
--export_type([stats/0]).
-
 %% Connection stats
 -define(CONNECTION_STATS, [
     'connections.count', % current connections
@@ -168,9 +174,9 @@ rec(Name, Secs, UpFun) ->
 cast(Msg) ->
     gen_server:cast(?SERVER, Msg).
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% gen_server callbacks
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 init(#{tick_ms := TickMs}) ->
     ok = emqx_tables:new(?TAB, [public, set, {write_concurrency, true}]),
@@ -201,7 +207,8 @@ handle_cast({setstat, Stat, MaxStat, Val}, State) ->
     safe_update_element(Stat, Val),
     {noreply, State};
 
-handle_cast({update_interval, Update = #update{name = Name}}, State = #state{updates = Updates}) ->
+handle_cast({update_interval, Update = #update{name = Name}},
+            State = #state{updates = Updates}) ->
     case lists:keyfind(Name, #update.name, Updates) of
         #update{} ->
             ?LOG(warning, "Duplicated update: ~s", [Name]),
@@ -242,9 +249,9 @@ terminate(_Reason, #state{timer = TRef}) ->
 code_change(_OldVsn, State, _Extra) ->
     {ok, State}.
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% Internal functions
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 safe_update_element(Key, Val) ->
     try ets:update_element(?TAB, Key, {2, Val}) of
@@ -256,3 +263,4 @@ safe_update_element(Key, Val) ->
         error:badarg ->
             ?LOG(warning, "Update ~p to ~p failed", [Key, Val])
     end.
+

+ 40 - 30
src/emqx_sup.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,11 +12,14 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_sup).
 
 -behaviour(supervisor).
 
+-include("types.hrl").
+
 -export([ start_link/0
         , start_child/1
         , start_child/2
@@ -28,29 +32,28 @@
                         | {ok, supervisor:child(), term()}
                         | {error, term()}).
 
--define(SUPERVISOR, ?MODULE).
+-define(SUP, ?MODULE).
 
 %%--------------------------------------------------------------------
 %% API
 %%--------------------------------------------------------------------
 
+-spec(start_link() -> startlink_ret()).
 start_link() ->
-    supervisor:start_link({local, ?SUPERVISOR}, ?MODULE, []).
+    supervisor:start_link({local, ?SUP}, ?MODULE, []).
 
 -spec(start_child(supervisor:child_spec()) -> startchild_ret()).
 start_child(ChildSpec) when is_tuple(ChildSpec) ->
-    supervisor:start_child(?SUPERVISOR, ChildSpec).
+    supervisor:start_child(?SUP, ChildSpec).
 
 -spec(start_child(module(), worker | supervisor) -> startchild_ret()).
-start_child(Mod, worker) ->
-    start_child(worker_spec(Mod));
-start_child(Mod, supervisor) ->
-    start_child(supervisor_spec(Mod)).
+start_child(Mod, Type) ->
+    start_child(child_spec(Mod, Type)).
 
 -spec(stop_child(supervisor:child_id()) -> ok | {error, term()}).
 stop_child(ChildId) ->
-    case supervisor:terminate_child(?SUPERVISOR, ChildId) of
-        ok    -> supervisor:delete_child(?SUPERVISOR, ChildId);
+    case supervisor:terminate_child(?SUP, ChildId) of
+        ok -> supervisor:delete_child(?SUP, ChildId);
         Error -> Error
     end.
 
@@ -60,32 +63,39 @@ stop_child(ChildId) ->
 
 init([]) ->
     %% Kernel Sup
-    KernelSup = supervisor_spec(emqx_kernel_sup),
+    KernelSup = child_spec(emqx_kernel_sup, supervisor),
     %% Router Sup
-    RouterSup = supervisor_spec(emqx_router_sup),
+    RouterSup = child_spec(emqx_router_sup, supervisor),
     %% Broker Sup
-    BrokerSup = supervisor_spec(emqx_broker_sup),
-    BridgeSup = supervisor_spec(emqx_bridge_sup),
-    %% Session Manager
-    SMSup = supervisor_spec(emqx_sm_sup),
-    %% Connection Manager
-    CMSup = supervisor_spec(emqx_cm_sup),
+    BrokerSup = child_spec(emqx_broker_sup, supervisor),
+    %% Bridge Sup
+    BridgeSup = child_spec(emqx_bridge_sup, supervisor),
+    %% CM Sup
+    CMSup = child_spec(emqx_cm_sup, supervisor),
     %% Sys Sup
-    SysSup = supervisor_spec(emqx_sys_sup),
+    SysSup = child_spec(emqx_sys_sup, supervisor),
     {ok, {{one_for_all, 0, 1},
-          [KernelSup,
-           RouterSup,
-           BrokerSup,
-           BridgeSup,
-           SMSup,
-           CMSup,
-           SysSup]}}.
+          [KernelSup, RouterSup, BrokerSup, BridgeSup, CMSup, SysSup]}}.
 
 %%--------------------------------------------------------------------
 %% Internal functions
 %%--------------------------------------------------------------------
 
-worker_spec(M) ->
-    {M, {M, start_link, []}, permanent, 30000, worker, [M]}.
-supervisor_spec(M) ->
-    {M, {M, start_link, []}, permanent, infinity, supervisor, [M]}.
+child_spec(Mod, supervisor) ->
+    #{id => Mod,
+      start => {Mod, start_link, []},
+      restart => permanent,
+      shutdown => infinity,
+      type => supervisor,
+      modules => [Mod]
+     };
+
+child_spec(Mod, worker) ->
+    #{id => Mod,
+      start => {Mod, start_link, []},
+      restart => permanent,
+      shutdown => 15000,
+      type => worker,
+      modules => [Mod]
+     }.
+

+ 3 - 1
src/emqx_sys.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_sys).
 

+ 6 - 8
src/emqx_sys_mon.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_sys_mon).
 
@@ -43,18 +45,14 @@
 
 -define(SYSMON, ?MODULE).
 
-%%------------------------------------------------------------------------------
-%% APIs
-%%------------------------------------------------------------------------------
-
-%% @doc Start system monitor
+%% @doc Start the system monitor.
 -spec(start_link(list(option())) -> startlink_ret()).
 start_link(Opts) ->
     gen_server:start_link({local, ?SYSMON}, ?MODULE, [Opts], []).
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% gen_server callbacks
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 init([Opts]) ->
     erlang:system_monitor(self(), parse_opt(Opts)),

+ 20 - 13
src/emqx_sys_sup.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_sys_sup).
 
@@ -24,23 +26,28 @@ start_link() ->
     supervisor:start_link({local, ?MODULE}, ?MODULE, []).
 
 init([]) ->
-    {ok, {{one_for_one, 10, 100}, [child_spec(emqx_sys, worker),
-                                   child_spec(emqx_sys_mon, worker, [emqx_config:get_env(sysmon, [])]),
-                                   child_spec(emqx_os_mon, worker, [emqx_config:get_env(os_mon, [])]),
-                                   child_spec(emqx_vm_mon, worker, [emqx_config:get_env(vm_mon, [])])]}}.
+    Childs = [child_spec(emqx_sys),
+              child_spec(emqx_sys_mon, [config(sysmon)]),
+              child_spec(emqx_os_mon,  [config(os_mon)]),
+              child_spec(emqx_vm_mon,  [config(vm_mon)])],
+    {ok, {{one_for_one, 10, 100}, Childs}}.
 
 %%--------------------------------------------------------------------
 %% Internal functions
 %%--------------------------------------------------------------------
 
-child_spec(M, worker) ->
-    child_spec(M, worker, []).
+child_spec(Mod) ->
+    child_spec(Mod, []).
 
-child_spec(M, worker, A) ->
-    #{id       => M,
-      start    => {M, start_link, A},
-      restart  => permanent,
+child_spec(Mod, Args) ->
+    #{id => Mod,
+      start => {Mod, start_link, Args},
+      restart => permanent,
       shutdown => 5000,
-      type     => worker,
-      modules  => [M]}.
+      type => worker,
+      modules => [Mod]
+     }.
+
+config(Name) ->
+    emqx_config:get_env(Name, []).
 

+ 4 - 1
src/emqx_tables.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_tables).
 
@@ -52,3 +54,4 @@ lookup_value(Tab, Key, Def) ->
     catch
         error:badarg -> Def
     end.
+

+ 4 - 1
src/emqx_time.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_time).
 
@@ -39,3 +41,4 @@ now_ms({MegaSecs, Secs, MicroSecs}) ->
 
 ts_from_ms(Ms) ->
     {Ms div 1000000, Ms rem 1000000, 0}.
+

+ 12 - 5
src/emqx_topic.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_topic).
 
@@ -33,19 +35,23 @@
         , parse/2
         ]).
 
+-export_type([ group/0
+             , topic/0
+             , word/0
+             , triple/0
+             ]).
+
 -type(group() :: binary()).
 -type(topic() :: binary()).
 -type(word() :: '' | '+' | '#' | binary()).
 -type(words() :: list(word())).
 -opaque(triple() :: {root | binary(), word(), binary()}).
 
--export_type([group/0, topic/0, word/0, triple/0]).
-
 -define(MAX_TOPIC_LEN, 4096).
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% APIs
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 %% @doc Is wildcard topic?
 -spec(wildcard(topic() | words()) -> true | false).
@@ -230,3 +236,4 @@ parse(Topic, Options = #{qos := QoS}) ->
     {Topic, Options#{rc => QoS}};
 parse(Topic, Options) ->
     {Topic, Options}.
+

+ 3 - 1
src/emqx_tracer.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_tracer).
 

+ 9 - 7
src/emqx_trie.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_trie).
 
@@ -35,9 +37,9 @@
 -define(TRIE, emqx_trie).
 -define(TRIE_NODE, emqx_trie_node).
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% Mnesia bootstrap
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 %% @doc Create or replicate trie tables.
 -spec(mnesia(boot | copy) -> ok).
@@ -64,9 +66,9 @@ mnesia(copy) ->
     %% Copy trie_node table
     ok = ekka_mnesia:copy_table(?TRIE_NODE).
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% Trie APIs
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 %% @doc Insert a topic filter into the trie.
 -spec(insert(emqx_topic:topic()) -> ok).
@@ -111,9 +113,9 @@ delete(Topic) when is_binary(Topic) ->
 empty() ->
     ets:info(?TRIE, size) == 0.
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% Internal functions
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 %% @private
 %% @doc Add a path to the trie.

+ 5 - 5
src/emqx_types.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_types).
 
@@ -32,9 +34,7 @@
              , protocol/0
              ]).
 
--export_type([ credentials/0
-             , session/0
-             ]).
+-export_type([credentials/0]).
 
 -export_type([ subscription/0
              , subscriber/0
@@ -65,7 +65,6 @@
                      share  => binary(),
                      atom() => term()
                     }).
--type(session() :: #session{}).
 -type(client_id() :: binary() | atom()).
 -type(username() :: maybe(binary())).
 -type(password() :: maybe(binary())).
@@ -105,3 +104,4 @@
 -type(alarm() :: #alarm{}).
 -type(plugin() :: #plugin{}).
 -type(command() :: #command{}).
+

+ 3 - 1
src/emqx_vm.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_vm).
 

+ 47 - 31
src/emqx_vm_mon.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,11 +12,14 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_vm_mon).
 
 -behaviour(gen_server).
 
+-include("logger.hrl").
+
 %% APIs
 -export([start_link/1]).
 
@@ -38,13 +42,13 @@
 
 -define(VM_MON, ?MODULE).
 
-%%----------------------------------------------------------------------
-%% API
-%%----------------------------------------------------------------------
-
 start_link(Opts) ->
     gen_server:start_link({local, ?VM_MON}, ?MODULE, [Opts], []).
 
+%%--------------------------------------------------------------------
+%% API
+%%--------------------------------------------------------------------
+
 get_check_interval() ->
     call(get_check_interval).
 
@@ -63,9 +67,12 @@ get_process_low_watermark() ->
 set_process_low_watermark(Float) ->
     call({set_process_low_watermark, Float}).
 
-%%----------------------------------------------------------------------
+call(Req) ->
+    gen_server:call(?VM_MON, Req, infinity).
+
+%%--------------------------------------------------------------------
 %% gen_server callbacks
-%%----------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 init([Opts]) ->
     {ok, ensure_check_timer(#{check_interval => proplists:get_value(check_interval, Opts, 30),
@@ -76,43 +83,53 @@ init([Opts]) ->
 
 handle_call(get_check_interval, _From, State) ->
     {reply, maps:get(check_interval, State, undefined), State};
+
 handle_call({set_check_interval, Seconds}, _From, State) ->
     {reply, ok, State#{check_interval := Seconds}};
 
 handle_call(get_process_high_watermark, _From, State) ->
     {reply, maps:get(process_high_watermark, State, undefined), State};
+
 handle_call({set_process_high_watermark, Float}, _From, State) ->
     {reply, ok, State#{process_high_watermark := Float}};
 
 handle_call(get_process_low_watermark, _From, State) ->
     {reply, maps:get(process_low_watermark, State, undefined), State};
+
 handle_call({set_process_low_watermark, Float}, _From, State) ->
     {reply, ok, State#{process_low_watermark := Float}};
 
-handle_call(_Request, _From, State) ->
-    {reply, ok, State}.
+handle_call(Req, _From, State) ->
+    ?LOG(error, "[VM_MON] Unexpected call: ~p", [Req]),
+    {reply, ignored, State}.
 
-handle_cast(_Request, State) ->
+handle_cast(Msg, State) ->
+    ?LOG(error, "[VM_MON] Unexpected cast: ~p", [Msg]),
     {noreply, State}.
 
-handle_info({timeout, Timer, check}, State = #{timer := Timer,
-                                               process_high_watermark := ProcHighWatermark,
-                                               process_low_watermark := ProcLowWatermark,
-                                               is_process_alarm_set := IsProcessAlarmSet}) ->
+handle_info({timeout, Timer, check},
+            State = #{timer := Timer,
+                      process_high_watermark := ProcHighWatermark,
+                      process_low_watermark := ProcLowWatermark,
+                      is_process_alarm_set := IsProcessAlarmSet}) ->
     ProcessCount = erlang:system_info(process_count),
-    case ProcessCount / erlang:system_info(process_limit) of
-        Percent when Percent >= ProcHighWatermark ->
-            alarm_handler:set_alarm({too_many_processes, ProcessCount}),
-            {noreply, ensure_check_timer(State#{is_process_alarm_set := true})};
-        Percent when Percent < ProcLowWatermark ->
-            case IsProcessAlarmSet of
-                true -> alarm_handler:clear_alarm(too_many_processes);
-                false -> ok
-            end,
-            {noreply, ensure_check_timer(State#{is_process_alarm_set := false})};
-        _Precent ->
-            {noreply, ensure_check_timer(State)}
-    end.
+    NState = case ProcessCount / erlang:system_info(process_limit) of
+                 Percent when Percent >= ProcHighWatermark ->
+                     alarm_handler:set_alarm({too_many_processes, ProcessCount}),
+                     State#{is_process_alarm_set := true};
+                 Percent when Percent < ProcLowWatermark ->
+                     case IsProcessAlarmSet of
+                         true -> alarm_handler:clear_alarm(too_many_processes);
+                         false -> ok
+                     end,
+                     State#{is_process_alarm_set := false};
+                 _Precent -> State
+             end,
+    {noreply, ensure_check_timer(NState)};
+
+handle_info(Info, State) ->
+    ?LOG(error, "[VM_MON] Unexpected info: ~p", [Info]),
+    {noreply, State}.
 
 terminate(_Reason, #{timer := Timer}) ->
     emqx_misc:cancel_timer(Timer).
@@ -120,11 +137,10 @@ terminate(_Reason, #{timer := Timer}) ->
 code_change(_OldVsn, State, _Extra) ->
     {ok, State}.
 
-%%----------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% Internal functions
-%%----------------------------------------------------------------------
-call(Req) ->
-    gen_server:call(?VM_MON, Req, infinity).
+%%--------------------------------------------------------------------
 
 ensure_check_timer(State = #{check_interval := Interval}) ->
     State#{timer := emqx_misc:start_timer(timer:seconds(Interval), check)}.
+

+ 24 - 11
src/emqx_ws_channel.erl

@@ -14,6 +14,7 @@
 %% limitations under the License.
 %%--------------------------------------------------------------------
 
+%% MQTT WebSocket Channel
 -module(emqx_ws_channel).
 
 -include("emqx.hrl").
@@ -148,8 +149,7 @@ websocket_init(#state{request = Req, options = Options}) ->
                        ?LOG(error, "Illegal cookie"),
                        undefined;
                    Error:Reason ->
-                       ?LOG(error,
-                            "Cookie is parsed failed, Error: ~p, Reason ~p",
+                       ?LOG(error, "Cookie is parsed failed, Error: ~p, Reason ~p",
                             [Error, Reason]),
                        undefined
                end,
@@ -171,7 +171,8 @@ websocket_init(#state{request = Req, options = Options}) ->
                 parse_state  = ParseState,
                 proto_state  = ProtoState,
                 enable_stats = EnableStats,
-                idle_timeout = IdleTimout}}.
+                idle_timeout = IdleTimout
+               }}.
 
 send_fun(WsPid) ->
     fun(Packet, Options) ->
@@ -247,10 +248,13 @@ websocket_info({call, From, session}, State = #state{proto_state = ProtoState})
     gen_server:reply(From, emqx_protocol:session(ProtoState)),
     {ok, State};
 
-websocket_info({deliver, PubOrAck}, State = #state{proto_state = ProtoState}) ->
-    case emqx_protocol:deliver(PubOrAck, ProtoState) of
-        {ok, ProtoState1} ->
-            {ok, ensure_stats_timer(State#state{proto_state = ProtoState1})};
+websocket_info(Delivery, State = #state{proto_state = ProtoState})
+  when element(1, Delivery) =:= deliver ->
+    case emqx_protocol:handle_out(Delivery, ProtoState) of
+        {ok, NProtoState} ->
+            {ok, State#state{proto_state = NProtoState}};
+        {ok, Packet, NProtoState} ->
+            handle_outgoing(Packet, State#state{proto_state = NProtoState});
         {error, Reason} ->
             shutdown(Reason, State)
     end;
@@ -290,8 +294,8 @@ websocket_info({shutdown, conflict, {ClientId, NewPid}}, State) ->
     ?LOG(warning, "Clientid '~s' conflict with ~p", [ClientId, NewPid]),
     shutdown(conflict, State);
 
-websocket_info({binary, Data}, State) ->
-    {reply, {binary, Data}, State};
+%% websocket_info({binary, Data}, State) ->
+%%    {reply, {binary, Data}, State};
 
 websocket_info({shutdown, Reason}, State) ->
     shutdown(Reason, State);
@@ -322,9 +326,12 @@ terminate(SockError, _Req, #state{keepalive   = Keepalive,
 %%--------------------------------------------------------------------
 
 handle_incoming(Packet, SuccFun, State = #state{proto_state = ProtoState}) ->
-    case emqx_protocol:received(Packet, ProtoState) of
+    case emqx_protocol:handle_in(Packet, ProtoState) of
         {ok, NProtoState} ->
             SuccFun(State#state{proto_state = NProtoState});
+        {ok, OutPacket, NProtoState} ->
+            %% TODO: How to call SuccFun???
+            handle_outgoing(OutPacket, State#state{proto_state = NProtoState});
         {error, Reason} ->
             ?LOG(error, "Protocol error: ~p", [Reason]),
             shutdown(Reason, State);
@@ -334,7 +341,12 @@ handle_incoming(Packet, SuccFun, State = #state{proto_state = ProtoState}) ->
             shutdown(Error, State#state{proto_state = NProtoState})
     end.
 
-
+handle_outgoing(Packet, State = #state{proto_state = _NProtoState}) ->
+    Data = emqx_frame:serialize(Packet), %% TODO:, Options),
+    BinSize = iolist_size(Data),
+    emqx_pd:update_counter(send_cnt, 1),
+    emqx_pd:update_counter(send_oct, BinSize),
+    {reply, {binary, Data}, ensure_stats_timer(State)}.
 
 ensure_stats_timer(State = #state{enable_stats = true,
                                   stats_timer  = undefined,
@@ -350,3 +362,4 @@ shutdown(Reason, State) ->
 
 wsock_stats() ->
     [{Key, emqx_pd:get_counter(Key)} || Key <- ?SOCK_STATS].
+

+ 16 - 10
src/emqx_zone.erl

@@ -1,4 +1,5 @@
-%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%--------------------------------------------------------------------
+%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -11,6 +12,7 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
+%%--------------------------------------------------------------------
 
 -module(emqx_zone).
 
@@ -43,28 +45,32 @@
         , code_change/3
         ]).
 
+-export_type([zone/0]).
+
 %% dummy state
 -record(state, {}).
 
+-type(zone() :: atom()).
+
 -define(TAB, ?MODULE).
 -define(SERVER, ?MODULE).
 -define(KEY(Zone, Key), {?MODULE, Zone, Key}).
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% APIs
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 -spec(start_link() -> startlink_ret()).
 start_link() ->
     gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
 
--spec(get_env(maybe(emqx_types:zone()), atom()) -> maybe(term())).
+-spec(get_env(maybe(zone()), atom()) -> maybe(term())).
 get_env(undefined, Key) ->
     emqx_config:get_env(Key);
 get_env(Zone, Key) ->
     get_env(Zone, Key, undefined).
 
--spec(get_env(maybe(emqx_types:zone()), atom(), term()) -> maybe(term())).
+-spec(get_env(maybe(zone()), atom(), term()) -> maybe(term())).
 get_env(undefined, Key, Def) ->
     emqx_config:get_env(Key, Def);
 get_env(Zone, Key, Def) ->
@@ -73,7 +79,7 @@ get_env(Zone, Key, Def) ->
         emqx_config:get_env(Key, Def)
     end.
 
--spec(set_env(emqx_types:zone(), atom(), term()) -> ok).
+-spec(set_env(zone(), atom(), term()) -> ok).
 set_env(Zone, Key, Val) ->
     gen_server:cast(?SERVER, {set_env, Zone, Key, Val}).
 
@@ -85,9 +91,9 @@ force_reload() ->
 stop() ->
     gen_server:stop(?SERVER, normal, infinity).
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% gen_server callbacks
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 init([]) ->
     _ = do_reload(),
@@ -119,9 +125,9 @@ terminate(_Reason, _State) ->
 code_change(_OldVsn, State, _Extra) ->
     {ok, State}.
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% Internal functions
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 do_reload() ->
     [ persistent_term:put(?KEY(Zone, Key), Val)

+ 8 - 16
test/emqx_ws_channel_SUITE.erl

@@ -23,11 +23,6 @@
 -include_lib("eunit/include/eunit.hrl").
 -include_lib("common_test/include/ct.hrl").
 
--define(CLIENT, ?CONNECT_PACKET(#mqtt_packet_connect{
-                                client_id = <<"mqtt_client">>,
-                                username  = <<"admin">>,
-                                password  = <<"public">>})).
-
 all() ->
     [ t_ws_connect_api
     , t_ws_auth_failure
@@ -45,17 +40,13 @@ t_ws_auth_failure(_Config) ->
     application:set_env(emqx, allow_anonymous, false),
     WS = rfc6455_client:new("ws://127.0.0.1:8083" ++ "/mqtt", self()),
     {ok, _} = rfc6455_client:open(WS),
-    Packet = raw_send_serialize(?CLIENT),
-    ok = rfc6455_client:send_binary(WS, Packet),
-    {binary, CONNACK} = rfc6455_client:recv(WS),
-    {ok, ?CONNACK_PACKET(?CONNACK_AUTH), <<>>, _} = raw_recv_pase(CONNACK),
-    application:set_env(emqx, allow_anonymous, true),
-    ok.
-
-t_ws_connect_api(_Config) ->
-    WS = rfc6455_client:new("ws://127.0.0.1:8083" ++ "/mqtt", self()),
-    {ok, _} = rfc6455_client:open(WS),
-    ok = rfc6455_client:send_binary(WS, raw_send_serialize(?CLIENT)),
+    Connect = ?CONNECT_PACKET(
+                 #mqtt_packet_connect{
+                    client_id = <<"mqtt_client">>,
+                    username  = <<"admin">>,
+                    password  = <<"public">>
+                   }),
+    ok = rfc6455_client:send_binary(WS, raw_send_serialize(Connect)),
     {binary, Bin} = rfc6455_client:recv(WS),
     Connack = ?CONNACK_PACKET(?CONNACK_ACCEPT),
     {ok, Connack, <<>>, _} = raw_recv_pase(Bin),
@@ -110,3 +101,4 @@ t_stats(StatsData) ->
     ?assertEqual(true, proplists:get_value(recv_pkt, StatsData) =:=1),
     ?assertEqual(true, proplists:get_value(recv_msg, StatsData) >=0),
     ?assertEqual(true, proplists:get_value(send_pkt, StatsData) =:=1).
+