From 58046016f02d02615711870fdb746ede415ec78f Mon Sep 17 00:00:00 2001 From: Chimit Date: Sat, 10 Dec 2016 13:29:37 +0800 Subject: [PATCH 001/125] Change parameters order --- etc/emq.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/emq.conf b/etc/emq.conf index c6f11331b..7d248d9f5 100644 --- a/etc/emq.conf +++ b/etc/emq.conf @@ -231,8 +231,8 @@ mqtt.listener.https = 8084 mqtt.listener.https.acceptors = 4 mqtt.listener.https.max_clients = 64 mqtt.listener.https.handshake_timeout = 15 -mqtt.listener.https.certfile = etc/certs/cert.pem mqtt.listener.https.keyfile = etc/certs/key.pem +mqtt.listener.https.certfile = etc/certs/cert.pem ## mqtt.listener.https.cacertfile = etc/certs/cacert.pem ## mqtt.listener.https.verify = verify_peer ## mqtt.listener.https.fail_if_no_peer_cert = true From 06ca13531edf9636e715800a157bd5ae2a32ec08 Mon Sep 17 00:00:00 2001 From: Chimit Date: Tue, 27 Dec 2016 17:21:27 +0800 Subject: [PATCH 002/125] Change "ok" message to uppercase --- src/emqttd_http.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/emqttd_http.erl b/src/emqttd_http.erl index e25f43e38..89fe01272 100644 --- a/src/emqttd_http.erl +++ b/src/emqttd_http.erl @@ -98,7 +98,7 @@ http_publish(Req) -> Msg = emqttd_message:make(ClientId, Qos, Topic, Payload), emqttd:publish(Msg#mqtt_message{retain = Retain}) end, Topics), - Req:ok({"text/plain", <<"ok">>}); + Req:ok({"text/plain", <<"OK">>}); {false, _} -> Req:respond({400, [], <<"Bad QoS">>}); {_, false} -> From b892bc703b9fb345a83b44a3a74e7fd81885a920 Mon Sep 17 00:00:00 2001 From: turtled Date: Wed, 11 Jan 2017 19:05:42 +0800 Subject: [PATCH 003/125] vars template emq.conf --- etc/emq.conf | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/etc/emq.conf b/etc/emq.conf index c6f11331b..9251b0f63 100644 --- a/etc/emq.conf +++ b/etc/emq.conf @@ -39,7 +39,7 @@ node.max_ets_tables = 256000 node.fullsweep_after = 1000 ## Crash dump -node.crash_dump = log/crash.dump +node.crash_dump = {{ runner_log_dir }}/crash.dump ## Distributed node ticktime node.dist_net_ticktime = 60 @@ -59,15 +59,15 @@ log.console = console log.console.level = error ## Console log file -## log.console.file = log/console.log +## log.console.file = {{ runner_log_dir }}/console.log ## Error log file -log.error.file = log/error.log +log.error.file = {{ runner_log_dir }}/error.log ## Enable the crash log. Enum: on, off log.crash = on -log.crash.file = log/crash.log +log.crash.file = {{ runner_log_dir }}/crash.log ##-------------------------------------------------------------------- ## MQTT Protocol @@ -86,7 +86,7 @@ mqtt.client_idle_timeout = 30 mqtt.allow_anonymous = true ## Default ACL File -mqtt.acl_file = etc/acl.conf +mqtt.acl_file = {{ runner_etc_dir }}/acl.conf ## Cache ACL for PUBLISH mqtt.cache_acl = true @@ -172,10 +172,10 @@ mqtt.bridge.ping_down_interval = 1 ##------------------------------------------------------------------- ## Dir of plugins' config -mqtt.plugins.etc_dir = etc/plugins/ +mqtt.plugins.etc_dir ={{ runner_etc_dir }}/plugins/ ## File to store loaded plugin names. -mqtt.plugins.loaded_file = data/loaded_plugins +mqtt.plugins.loaded_file = {{ runner_data_dir }}/loaded_plugins ##-------------------------------------------------------------------- ## MQTT Listeners @@ -215,9 +215,9 @@ mqtt.listener.ssl.max_clients = 512 ## Configuring SSL Options ## See http://erlang.org/doc/man/ssl.html mqtt.listener.ssl.handshake_timeout = 15 -mqtt.listener.ssl.keyfile = etc/certs/key.pem -mqtt.listener.ssl.certfile = etc/certs/cert.pem -## mqtt.listener.ssl.cacertfile = etc/certs/cacert.pem +mqtt.listener.ssl.keyfile = {{ runner_etc_dir }}/certs/key.pem +mqtt.listener.ssl.certfile = {{ runner_etc_dir }}/certs/cert.pem +## mqtt.listener.ssl.cacertfile = {{ runner_etc_dir }}/certs/cacert.pem ## mqtt.listener.ssl.verify = verify_peer ## mqtt.listener.ssl.fail_if_no_peer_cert = true @@ -231,9 +231,9 @@ mqtt.listener.https = 8084 mqtt.listener.https.acceptors = 4 mqtt.listener.https.max_clients = 64 mqtt.listener.https.handshake_timeout = 15 -mqtt.listener.https.certfile = etc/certs/cert.pem -mqtt.listener.https.keyfile = etc/certs/key.pem -## mqtt.listener.https.cacertfile = etc/certs/cacert.pem +mqtt.listener.https.keyfile = {{ runner_etc_dir }}/certs/key.pem +mqtt.listener.https.certfile = {{ runner_etc_dir }}/certs/cert.pem +## mqtt.listener.https.cacertfile = {{ runner_etc_dir }}/certs/cacert.pem ## mqtt.listener.https.verify = verify_peer ## mqtt.listener.https.fail_if_no_peer_cert = true From 8092543efba48926e82f95a7370aae4dc128add7 Mon Sep 17 00:00:00 2001 From: huangdan Date: Wed, 11 Jan 2017 13:24:40 +0800 Subject: [PATCH 004/125] Copyright (c) 2012-2017 --- include/emqttd.hrl | 4 ++-- include/emqttd_cli.hrl | 2 +- include/emqttd_internal.hrl | 2 +- include/emqttd_protocol.hrl | 2 +- include/emqttd_trie.hrl | 2 +- src/emqttd.erl | 2 +- src/emqttd_access_control.erl | 2 +- src/emqttd_access_rule.erl | 2 +- src/emqttd_acl_internal.erl | 2 +- src/emqttd_acl_mod.erl | 2 +- src/emqttd_alarm.erl | 2 +- src/emqttd_app.erl | 2 +- src/emqttd_auth_mod.erl | 2 +- src/emqttd_base62.erl | 2 +- src/emqttd_boot.erl | 2 +- src/emqttd_bridge.erl | 2 +- src/emqttd_bridge_sup.erl | 2 +- src/emqttd_bridge_sup_sup.erl | 2 +- src/emqttd_broker.erl | 2 +- src/emqttd_cli.erl | 2 +- src/emqttd_client.erl | 2 +- src/emqttd_cluster.erl | 2 +- src/emqttd_cm.erl | 2 +- src/emqttd_cm_sup.erl | 2 +- src/emqttd_ctl.erl | 2 +- src/emqttd_gen_mod.erl | 2 +- src/emqttd_guid.erl | 2 +- src/emqttd_http.erl | 2 +- src/emqttd_keepalive.erl | 2 +- src/emqttd_message.erl | 2 +- src/emqttd_metrics.erl | 2 +- src/emqttd_mnesia.erl | 2 +- src/emqttd_mod_sup.erl | 2 +- src/emqttd_mqueue.erl | 2 +- src/emqttd_net.erl | 2 +- src/emqttd_node.erl | 2 +- src/emqttd_opts.erl | 2 +- src/emqttd_packet.erl | 2 +- src/emqttd_parser.erl | 2 +- src/emqttd_plugins.erl | 2 +- src/emqttd_pool_sup.erl | 2 +- src/emqttd_pooler.erl | 2 +- src/emqttd_protocol.erl | 2 +- src/emqttd_pubsub.erl | 2 +- src/emqttd_pubsub_sup.erl | 2 +- src/emqttd_router.erl | 2 +- src/emqttd_serializer.erl | 2 +- src/emqttd_server.erl | 2 +- src/emqttd_session.erl | 2 +- src/emqttd_session_sup.erl | 2 +- src/emqttd_sm.erl | 2 +- src/emqttd_sm_helper.erl | 2 +- src/emqttd_sm_sup.erl | 2 +- src/emqttd_stats.erl | 2 +- src/emqttd_sup.erl | 2 +- src/emqttd_sysmon.erl | 2 +- src/emqttd_sysmon_sup.erl | 2 +- src/emqttd_time.erl | 2 +- src/emqttd_topic.erl | 2 +- src/emqttd_trace.erl | 2 +- src/emqttd_trace_sup.erl | 2 +- src/emqttd_trie.erl | 2 +- src/emqttd_vm.erl | 2 +- src/emqttd_ws.erl | 2 +- src/emqttd_ws_client.erl | 2 +- src/emqttd_ws_client_sup.erl | 2 +- src/lager_emqtt_backend.erl | 2 +- test/emqttd_SUITE.erl | 2 +- test/emqttd_access_SUITE.erl | 2 +- test/emqttd_acl_test_mod.erl | 2 +- test/emqttd_auth_anonymous_test_mod.erl | 2 +- test/emqttd_auth_dashboard.erl | 2 +- test/emqttd_lib_SUITE.erl | 2 +- test/emqttd_mod_SUITE.erl | 2 +- test/emqttd_mqueue_SUITE.erl | 2 +- test/emqttd_net_SUITE.erl | 2 +- test/emqttd_protocol_SUITE.erl | 2 +- test/emqttd_topic_SUITE.erl | 2 +- test/emqttd_trie_SUITE.erl | 2 +- test/emqttd_vm_SUITE.erl | 2 +- 80 files changed, 81 insertions(+), 81 deletions(-) diff --git a/include/emqttd.hrl b/include/emqttd.hrl index 75a030c2e..53350ed6d 100644 --- a/include/emqttd.hrl +++ b/include/emqttd.hrl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ %% Banner %%-------------------------------------------------------------------- --define(COPYRIGHT, "Copyright (C) 2012-2016, Feng Lee "). +-define(COPYRIGHT, "Copyright (C) 2012-2017, Feng Lee "). -define(LICENSE_MESSAGE, "Licensed under the Apache License, Version 2.0"). diff --git a/include/emqttd_cli.hrl b/include/emqttd_cli.hrl index f8245f9a8..461306747 100644 --- a/include/emqttd_cli.hrl +++ b/include/emqttd_cli.hrl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/include/emqttd_internal.hrl b/include/emqttd_internal.hrl index 56522a62f..06102e26a 100644 --- a/include/emqttd_internal.hrl +++ b/include/emqttd_internal.hrl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/include/emqttd_protocol.hrl b/include/emqttd_protocol.hrl index 8a5e5d0ca..9ed3a993b 100644 --- a/include/emqttd_protocol.hrl +++ b/include/emqttd_protocol.hrl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/include/emqttd_trie.hrl b/include/emqttd_trie.hrl index e701d90cd..bd4184ad8 100644 --- a/include/emqttd_trie.hrl +++ b/include/emqttd_trie.hrl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2016 Feng Lee . +%% Copyright (c) 2016-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd.erl b/src/emqttd.erl index 8128d0789..23db807cc 100644 --- a/src/emqttd.erl +++ b/src/emqttd.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_access_control.erl b/src/emqttd_access_control.erl index 688e244a4..7aea58e79 100644 --- a/src/emqttd_access_control.erl +++ b/src/emqttd_access_control.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_access_rule.erl b/src/emqttd_access_rule.erl index 88395b182..1590d2efe 100644 --- a/src/emqttd_access_rule.erl +++ b/src/emqttd_access_rule.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_acl_internal.erl b/src/emqttd_acl_internal.erl index 610cf91a3..74ab8519a 100644 --- a/src/emqttd_acl_internal.erl +++ b/src/emqttd_acl_internal.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_acl_mod.erl b/src/emqttd_acl_mod.erl index 2eb09a0fe..44c775460 100644 --- a/src/emqttd_acl_mod.erl +++ b/src/emqttd_acl_mod.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_alarm.erl b/src/emqttd_alarm.erl index 4ba752035..1ff4a5ae9 100644 --- a/src/emqttd_alarm.erl +++ b/src/emqttd_alarm.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_app.erl b/src/emqttd_app.erl index 048a196cd..bdbfc4a54 100644 --- a/src/emqttd_app.erl +++ b/src/emqttd_app.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_auth_mod.erl b/src/emqttd_auth_mod.erl index 845d5a0fb..d6d4a55b0 100644 --- a/src/emqttd_auth_mod.erl +++ b/src/emqttd_auth_mod.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_base62.erl b/src/emqttd_base62.erl index 1e9d0a1a2..5895fa245 100644 --- a/src/emqttd_base62.erl +++ b/src/emqttd_base62.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2016 Feng Lee . +%% Copyright (c) 2016-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_boot.erl b/src/emqttd_boot.erl index e2ca2b931..043eb08c3 100644 --- a/src/emqttd_boot.erl +++ b/src/emqttd_boot.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_bridge.erl b/src/emqttd_bridge.erl index de51b8f5d..3dca7bf0f 100644 --- a/src/emqttd_bridge.erl +++ b/src/emqttd_bridge.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_bridge_sup.erl b/src/emqttd_bridge_sup.erl index 64c3fcf83..dd418b2f6 100644 --- a/src/emqttd_bridge_sup.erl +++ b/src/emqttd_bridge_sup.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_bridge_sup_sup.erl b/src/emqttd_bridge_sup_sup.erl index 109f94764..47e246273 100644 --- a/src/emqttd_bridge_sup_sup.erl +++ b/src/emqttd_bridge_sup_sup.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_broker.erl b/src/emqttd_broker.erl index 409fe5c8b..96dd7d553 100644 --- a/src/emqttd_broker.erl +++ b/src/emqttd_broker.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_cli.erl b/src/emqttd_cli.erl index 547f25e81..085032b24 100644 --- a/src/emqttd_cli.erl +++ b/src/emqttd_cli.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_client.erl b/src/emqttd_client.erl index 931343efe..6f15bdf8c 100644 --- a/src/emqttd_client.erl +++ b/src/emqttd_client.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_cluster.erl b/src/emqttd_cluster.erl index d5494a6b9..7c83c079c 100644 --- a/src/emqttd_cluster.erl +++ b/src/emqttd_cluster.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_cm.erl b/src/emqttd_cm.erl index da98dedd6..6b40a480f 100644 --- a/src/emqttd_cm.erl +++ b/src/emqttd_cm.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_cm_sup.erl b/src/emqttd_cm_sup.erl index 0def00005..dfa623cc3 100644 --- a/src/emqttd_cm_sup.erl +++ b/src/emqttd_cm_sup.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_ctl.erl b/src/emqttd_ctl.erl index 1cd5d3055..c0b7dcf70 100644 --- a/src/emqttd_ctl.erl +++ b/src/emqttd_ctl.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_gen_mod.erl b/src/emqttd_gen_mod.erl index 043c6e177..b201f7bdd 100644 --- a/src/emqttd_gen_mod.erl +++ b/src/emqttd_gen_mod.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_guid.erl b/src/emqttd_guid.erl index 03a4e6904..a4c2d1ea4 100644 --- a/src/emqttd_guid.erl +++ b/src/emqttd_guid.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_http.erl b/src/emqttd_http.erl index e25f43e38..69b9f827a 100644 --- a/src/emqttd_http.erl +++ b/src/emqttd_http.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_keepalive.erl b/src/emqttd_keepalive.erl index 0e79622c4..54a4d20e5 100644 --- a/src/emqttd_keepalive.erl +++ b/src/emqttd_keepalive.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_message.erl b/src/emqttd_message.erl index 7dfc4fdc6..ec4a02479 100644 --- a/src/emqttd_message.erl +++ b/src/emqttd_message.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_metrics.erl b/src/emqttd_metrics.erl index 651e5037a..6f7a53f5a 100644 --- a/src/emqttd_metrics.erl +++ b/src/emqttd_metrics.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_mnesia.erl b/src/emqttd_mnesia.erl index 768d27cf0..52dca1d1b 100644 --- a/src/emqttd_mnesia.erl +++ b/src/emqttd_mnesia.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_mod_sup.erl b/src/emqttd_mod_sup.erl index a0f3cb9d3..57b13bc37 100644 --- a/src/emqttd_mod_sup.erl +++ b/src/emqttd_mod_sup.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_mqueue.erl b/src/emqttd_mqueue.erl index a554c9c83..53f37206f 100644 --- a/src/emqttd_mqueue.erl +++ b/src/emqttd_mqueue.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_net.erl b/src/emqttd_net.erl index a57e4a6ec..8278e1f74 100644 --- a/src/emqttd_net.erl +++ b/src/emqttd_net.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_node.erl b/src/emqttd_node.erl index 0b492253f..e48f71fae 100644 --- a/src/emqttd_node.erl +++ b/src/emqttd_node.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_opts.erl b/src/emqttd_opts.erl index b2250bad6..8f5f3fea5 100644 --- a/src/emqttd_opts.erl +++ b/src/emqttd_opts.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_packet.erl b/src/emqttd_packet.erl index 618b6730b..38de31fe0 100644 --- a/src/emqttd_packet.erl +++ b/src/emqttd_packet.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_parser.erl b/src/emqttd_parser.erl index 139c5c1fb..d6c09c42e 100644 --- a/src/emqttd_parser.erl +++ b/src/emqttd_parser.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_plugins.erl b/src/emqttd_plugins.erl index 0657b7799..78b543440 100644 --- a/src/emqttd_plugins.erl +++ b/src/emqttd_plugins.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_pool_sup.erl b/src/emqttd_pool_sup.erl index b47199cb0..e06d09e8f 100644 --- a/src/emqttd_pool_sup.erl +++ b/src/emqttd_pool_sup.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_pooler.erl b/src/emqttd_pooler.erl index a028b0046..2701c470e 100644 --- a/src/emqttd_pooler.erl +++ b/src/emqttd_pooler.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_protocol.erl b/src/emqttd_protocol.erl index 038d30b91..e892c6158 100644 --- a/src/emqttd_protocol.erl +++ b/src/emqttd_protocol.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_pubsub.erl b/src/emqttd_pubsub.erl index f4d6859a4..83a080ff6 100644 --- a/src/emqttd_pubsub.erl +++ b/src/emqttd_pubsub.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_pubsub_sup.erl b/src/emqttd_pubsub_sup.erl index 988dc6553..882fce57c 100644 --- a/src/emqttd_pubsub_sup.erl +++ b/src/emqttd_pubsub_sup.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_router.erl b/src/emqttd_router.erl index 5a77c9914..e0e3962a2 100644 --- a/src/emqttd_router.erl +++ b/src/emqttd_router.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_serializer.erl b/src/emqttd_serializer.erl index 00573067f..f08ed7eef 100644 --- a/src/emqttd_serializer.erl +++ b/src/emqttd_serializer.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_server.erl b/src/emqttd_server.erl index 83903736e..f21bd5d4b 100644 --- a/src/emqttd_server.erl +++ b/src/emqttd_server.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_session.erl b/src/emqttd_session.erl index 2313cadb7..a73503479 100644 --- a/src/emqttd_session.erl +++ b/src/emqttd_session.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_session_sup.erl b/src/emqttd_session_sup.erl index 394cb84d0..88766b11a 100644 --- a/src/emqttd_session_sup.erl +++ b/src/emqttd_session_sup.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_sm.erl b/src/emqttd_sm.erl index 3a978b3fd..704242b2d 100644 --- a/src/emqttd_sm.erl +++ b/src/emqttd_sm.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_sm_helper.erl b/src/emqttd_sm_helper.erl index aa6a7e365..97b356c8d 100644 --- a/src/emqttd_sm_helper.erl +++ b/src/emqttd_sm_helper.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_sm_sup.erl b/src/emqttd_sm_sup.erl index 1935bcfde..c43f89f80 100644 --- a/src/emqttd_sm_sup.erl +++ b/src/emqttd_sm_sup.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_stats.erl b/src/emqttd_stats.erl index 9ffc08299..cae57207f 100644 --- a/src/emqttd_stats.erl +++ b/src/emqttd_stats.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_sup.erl b/src/emqttd_sup.erl index 709f30adb..59ffde2ea 100644 --- a/src/emqttd_sup.erl +++ b/src/emqttd_sup.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_sysmon.erl b/src/emqttd_sysmon.erl index 43db85de6..7c338a842 100644 --- a/src/emqttd_sysmon.erl +++ b/src/emqttd_sysmon.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_sysmon_sup.erl b/src/emqttd_sysmon_sup.erl index 9ba73b41e..0131c42db 100644 --- a/src/emqttd_sysmon_sup.erl +++ b/src/emqttd_sysmon_sup.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_time.erl b/src/emqttd_time.erl index 0ade04f6f..49cf4965c 100644 --- a/src/emqttd_time.erl +++ b/src/emqttd_time.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_topic.erl b/src/emqttd_topic.erl index 2a198c2e1..0afeacae8 100644 --- a/src/emqttd_topic.erl +++ b/src/emqttd_topic.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_trace.erl b/src/emqttd_trace.erl index ad938c502..069f44937 100644 --- a/src/emqttd_trace.erl +++ b/src/emqttd_trace.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_trace_sup.erl b/src/emqttd_trace_sup.erl index f0402a257..22736b1a2 100644 --- a/src/emqttd_trace_sup.erl +++ b/src/emqttd_trace_sup.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_trie.erl b/src/emqttd_trie.erl index 7266ea4b6..23bb40bcf 100644 --- a/src/emqttd_trie.erl +++ b/src/emqttd_trie.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_vm.erl b/src/emqttd_vm.erl index bdc6cef40..abf4d3b78 100644 --- a/src/emqttd_vm.erl +++ b/src/emqttd_vm.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_ws.erl b/src/emqttd_ws.erl index cd48a45c1..a5b3b344f 100644 --- a/src/emqttd_ws.erl +++ b/src/emqttd_ws.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_ws_client.erl b/src/emqttd_ws_client.erl index 12968dceb..bd416fa27 100644 --- a/src/emqttd_ws_client.erl +++ b/src/emqttd_ws_client.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_ws_client_sup.erl b/src/emqttd_ws_client_sup.erl index 3af715337..d01055115 100644 --- a/src/emqttd_ws_client_sup.erl +++ b/src/emqttd_ws_client_sup.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/lager_emqtt_backend.erl b/src/lager_emqtt_backend.erl index c98e87de9..214182b8d 100644 --- a/src/lager_emqtt_backend.erl +++ b/src/lager_emqtt_backend.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/test/emqttd_SUITE.erl b/test/emqttd_SUITE.erl index 45fa13c50..a7ab2fb5f 100644 --- a/test/emqttd_SUITE.erl +++ b/test/emqttd_SUITE.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/test/emqttd_access_SUITE.erl b/test/emqttd_access_SUITE.erl index e127cf601..5ab7e992b 100644 --- a/test/emqttd_access_SUITE.erl +++ b/test/emqttd_access_SUITE.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/test/emqttd_acl_test_mod.erl b/test/emqttd_acl_test_mod.erl index c9ddf057e..196337fa4 100644 --- a/test/emqttd_acl_test_mod.erl +++ b/test/emqttd_acl_test_mod.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/test/emqttd_auth_anonymous_test_mod.erl b/test/emqttd_auth_anonymous_test_mod.erl index 8424b467a..8e93be0bc 100644 --- a/test/emqttd_auth_anonymous_test_mod.erl +++ b/test/emqttd_auth_anonymous_test_mod.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/test/emqttd_auth_dashboard.erl b/test/emqttd_auth_dashboard.erl index d36390a09..0e509c08a 100644 --- a/test/emqttd_auth_dashboard.erl +++ b/test/emqttd_auth_dashboard.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/test/emqttd_lib_SUITE.erl b/test/emqttd_lib_SUITE.erl index 51dd499ba..e51e778f0 100644 --- a/test/emqttd_lib_SUITE.erl +++ b/test/emqttd_lib_SUITE.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/test/emqttd_mod_SUITE.erl b/test/emqttd_mod_SUITE.erl index 9240b8a3f..a258eabe0 100644 --- a/test/emqttd_mod_SUITE.erl +++ b/test/emqttd_mod_SUITE.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2016 Feng Lee . +%% Copyright (c) 2016-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/test/emqttd_mqueue_SUITE.erl b/test/emqttd_mqueue_SUITE.erl index 76ab62044..eaa0ecc68 100644 --- a/test/emqttd_mqueue_SUITE.erl +++ b/test/emqttd_mqueue_SUITE.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/test/emqttd_net_SUITE.erl b/test/emqttd_net_SUITE.erl index 22641c2b2..7d70f4291 100644 --- a/test/emqttd_net_SUITE.erl +++ b/test/emqttd_net_SUITE.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2016 Feng Lee . +%% Copyright (c) 2016-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/test/emqttd_protocol_SUITE.erl b/test/emqttd_protocol_SUITE.erl index 96b969278..49189bb90 100644 --- a/test/emqttd_protocol_SUITE.erl +++ b/test/emqttd_protocol_SUITE.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/test/emqttd_topic_SUITE.erl b/test/emqttd_topic_SUITE.erl index 5692dbb43..f8be7df1a 100644 --- a/test/emqttd_topic_SUITE.erl +++ b/test/emqttd_topic_SUITE.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2016 Feng Lee . +%% Copyright (c) 2016-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/test/emqttd_trie_SUITE.erl b/test/emqttd_trie_SUITE.erl index ceda3abcb..37d247755 100644 --- a/test/emqttd_trie_SUITE.erl +++ b/test/emqttd_trie_SUITE.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/test/emqttd_vm_SUITE.erl b/test/emqttd_vm_SUITE.erl index a26bde4f4..ba56ef97f 100644 --- a/test/emqttd_vm_SUITE.erl +++ b/test/emqttd_vm_SUITE.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2016 Feng Lee . +%% Copyright (c) 2012-2017 Feng Lee . %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. From 56992cd48bd22a9ed9ab9224dab4d28f104e736f Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Wed, 11 Jan 2017 15:29:24 +0800 Subject: [PATCH 005/125] Bump version to 2.0.7 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index a4591784e..82dc2a0d5 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ PROJECT = emqttd PROJECT_DESCRIPTION = Erlang MQTT Broker -PROJECT_VERSION = 2.0.6 +PROJECT_VERSION = 2.0.7 DEPS = gproc lager gen_logger esockd mochiweb From 101532980bc37b48880463d10b14c1877fea4dee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E4=B8=B9?= Date: Wed, 11 Jan 2017 21:38:34 +0800 Subject: [PATCH 006/125] Update emqttd_pmon.erl Copyright (c) 2016-2017 --- src/emqttd_pmon.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/emqttd_pmon.erl b/src/emqttd_pmon.erl index dc9554015..e5f286eea 100644 --- a/src/emqttd_pmon.erl +++ b/src/emqttd_pmon.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2016 Feng Lee . All Rights Reserved. +%% Copyright (c) 2016-2017 Feng Lee . 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. From 72ed2919470cb48d18c703037206b2c301fed55b Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Thu, 12 Jan 2017 11:56:30 +0800 Subject: [PATCH 007/125] Replace the 'runner_' variables with 'platform_' --- etc/emq.conf | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/etc/emq.conf b/etc/emq.conf index 4303bc034..7238f3395 100644 --- a/etc/emq.conf +++ b/etc/emq.conf @@ -39,7 +39,7 @@ node.max_ets_tables = 256000 node.fullsweep_after = 1000 ## Crash dump -node.crash_dump = {{ runner_log_dir }}/crash.dump +node.crash_dump = {{ platform_log_dir }}/crash.dump ## Distributed node ticktime node.dist_net_ticktime = 60 @@ -59,15 +59,15 @@ log.console = console log.console.level = error ## Console log file -## log.console.file = {{ runner_log_dir }}/console.log +## log.console.file = {{ platform_log_dir }}/console.log ## Error log file -log.error.file = {{ runner_log_dir }}/error.log +log.error.file = {{ platform_log_dir }}/error.log ## Enable the crash log. Enum: on, off log.crash = on -log.crash.file = {{ runner_log_dir }}/crash.log +log.crash.file = {{ platform_log_dir }}/crash.log ##-------------------------------------------------------------------- ## MQTT Protocol @@ -86,7 +86,7 @@ mqtt.client_idle_timeout = 30 mqtt.allow_anonymous = true ## Default ACL File -mqtt.acl_file = {{ runner_etc_dir }}/acl.conf +mqtt.acl_file = {{ platform_etc_dir }}/acl.conf ## Cache ACL for PUBLISH mqtt.cache_acl = true @@ -172,10 +172,10 @@ mqtt.bridge.ping_down_interval = 1 ##------------------------------------------------------------------- ## Dir of plugins' config -mqtt.plugins.etc_dir ={{ runner_etc_dir }}/plugins/ +mqtt.plugins.etc_dir ={{ platform_etc_dir }}/plugins/ ## File to store loaded plugin names. -mqtt.plugins.loaded_file = {{ runner_data_dir }}/loaded_plugins +mqtt.plugins.loaded_file = {{ platform_data_dir }}/loaded_plugins ##-------------------------------------------------------------------- ## MQTT Listeners @@ -215,9 +215,9 @@ mqtt.listener.ssl.max_clients = 512 ## Configuring SSL Options ## See http://erlang.org/doc/man/ssl.html mqtt.listener.ssl.handshake_timeout = 15 -mqtt.listener.ssl.keyfile = {{ runner_etc_dir }}/certs/key.pem -mqtt.listener.ssl.certfile = {{ runner_etc_dir }}/certs/cert.pem -## mqtt.listener.ssl.cacertfile = {{ runner_etc_dir }}/certs/cacert.pem +mqtt.listener.ssl.keyfile = {{ platform_etc_dir }}/certs/key.pem +mqtt.listener.ssl.certfile = {{ platform_etc_dir }}/certs/cert.pem +## mqtt.listener.ssl.cacertfile = {{ platform_etc_dir }}/certs/cacert.pem ## mqtt.listener.ssl.verify = verify_peer ## mqtt.listener.ssl.fail_if_no_peer_cert = true @@ -231,9 +231,9 @@ mqtt.listener.https = 8084 mqtt.listener.https.acceptors = 4 mqtt.listener.https.max_clients = 64 mqtt.listener.https.handshake_timeout = 15 -mqtt.listener.https.keyfile = {{ runner_etc_dir }}/certs/key.pem -mqtt.listener.https.certfile = {{ runner_etc_dir }}/certs/cert.pem -## mqtt.listener.https.cacertfile = {{ runner_etc_dir }}/certs/cacert.pem +mqtt.listener.https.keyfile = {{ platform_etc_dir }}/certs/key.pem +mqtt.listener.https.certfile = {{ platform_etc_dir }}/certs/cert.pem +## mqtt.listener.https.cacertfile = {{ platform_etc_dir }}/certs/cacert.pem ## mqtt.listener.https.verify = verify_peer ## mqtt.listener.https.fail_if_no_peer_cert = true From ff6acbf29370bc2151b9b3bf8a61f3ad96eaab92 Mon Sep 17 00:00:00 2001 From: Lei Chen Date: Mon, 16 Jan 2017 16:24:19 +0800 Subject: [PATCH 008/125] Client unsubscribe hook does not handle 'stop' value. Insert a statement to prevent calling emqttd_session:unsubscribe() if hook returns stop. --- src/emqttd_protocol.erl | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/emqttd_protocol.erl b/src/emqttd_protocol.erl index e892c6158..2b524d1a9 100644 --- a/src/emqttd_protocol.erl +++ b/src/emqttd_protocol.erl @@ -130,9 +130,12 @@ handle({subscribe, RawTopicTable}, ProtoState = #proto_state{client_id = ClientI handle({unsubscribe, RawTopics}, ProtoState = #proto_state{client_id = ClientId, username = Username, session = Session}) -> - {ok, TopicTable} = emqttd:run_hooks('client.unsubscribe', - [ClientId, Username], parse_topics(RawTopics)), - emqttd_session:unsubscribe(Session, TopicTable), + case emqttd:run_hooks('client.unsubscribe', [ClientId, Username], parse_topics(RawTopics)) of + {ok, TopicTable} -> + emqttd_session:unsubscribe(Session, TopicTable); + {stop, _} -> + ok + end, {ok, ProtoState}. process(Packet = ?CONNECT_PACKET(Var), State0) -> @@ -243,8 +246,13 @@ process(?UNSUBSCRIBE_PACKET(PacketId, []), State) -> process(?UNSUBSCRIBE_PACKET(PacketId, RawTopics), State = #proto_state{ client_id = ClientId, username = Username, session = Session}) -> - {ok, TopicTable} = emqttd:run_hooks('client.unsubscribe', [ClientId, Username], parse_topics(RawTopics)), - emqttd_session:unsubscribe(Session, TopicTable), + case emqttd:run_hooks('client.unsubscribe', [ClientId, Username], parse_topics(RawTopics)) of + {ok, TopicTable} -> + emqttd_session:unsubscribe(Session, TopicTable); + {stop, _} -> + ok + end, + , send(?UNSUBACK_PACKET(PacketId), State); process(?PACKET(?PINGREQ), State) -> From 4606eaeb5859b401696b88aab2c3e1fbba7c6366 Mon Sep 17 00:00:00 2001 From: Lei Chen Date: Mon, 16 Jan 2017 16:27:10 +0800 Subject: [PATCH 009/125] delete a comma. --- src/emqttd_protocol.erl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/emqttd_protocol.erl b/src/emqttd_protocol.erl index 2b524d1a9..f0b877dc5 100644 --- a/src/emqttd_protocol.erl +++ b/src/emqttd_protocol.erl @@ -252,7 +252,6 @@ process(?UNSUBSCRIBE_PACKET(PacketId, RawTopics), State = #proto_state{ {stop, _} -> ok end, - , send(?UNSUBACK_PACKET(PacketId), State); process(?PACKET(?PINGREQ), State) -> From 8e60b8f00a940e71c39469ad489f71a7516b72e0 Mon Sep 17 00:00:00 2001 From: Drew Varner Date: Mon, 23 Jan 2017 20:02:02 -0500 Subject: [PATCH 010/125] Remove gen_logger references --- Makefile | 3 +-- rebar.config | 5 ++++- src/emqttd_boot.erl | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 82dc2a0d5..a4e02ce45 100644 --- a/Makefile +++ b/Makefile @@ -2,12 +2,11 @@ PROJECT = emqttd PROJECT_DESCRIPTION = Erlang MQTT Broker PROJECT_VERSION = 2.0.7 -DEPS = gproc lager gen_logger esockd mochiweb +DEPS = gproc lager esockd mochiweb dep_gproc = git https://github.com/uwiger/gproc dep_getopt = git https://github.com/jcomellas/getopt v0.8.2 dep_lager = git https://github.com/basho/lager master -dep_gen_logger = git https://github.com/emqtt/gen_logger dep_esockd = git https://github.com/emqtt/esockd master dep_mochiweb = git https://github.com/emqtt/mochiweb diff --git a/rebar.config b/rebar.config index c9e0dbcf0..af42c962a 100644 --- a/rebar.config +++ b/rebar.config @@ -1,4 +1,7 @@ {deps, [ -{gproc,".*",{git,"https://github.com/uwiger/gproc",""}},{lager,".*",{git,"https://github.com/basho/lager","master"}},{gen_logger,".*",{git,"https://github.com/emqtt/gen_logger",""}},{esockd,".*",{git,"https://github.com/emqtt/esockd","master"}},{mochiweb,".*",{git,"https://github.com/emqtt/mochiweb",""}} + {gproc,".*",{git,"https://github.com/uwiger/gproc",""}}, + {lager,".*",{git,"https://github.com/basho/lager","master"}}, + {esockd,".*",{git,"https://github.com/emqtt/esockd","master"}}, + {mochiweb,".*",{git,"https://github.com/emqtt/mochiweb",""}} ]}. {erl_opts, [{parse_transform,lager_transform}]}. diff --git a/src/emqttd_boot.erl b/src/emqttd_boot.erl index 043eb08c3..dd1b8a65b 100644 --- a/src/emqttd_boot.erl +++ b/src/emqttd_boot.erl @@ -58,6 +58,6 @@ ignore_lib_apps(Apps) -> snmp, otp_mibs, public_key, asn1, ssh, hipe, common_test, observer, webtool, xmerl, tools, test_server, compiler, debugger, eunit, et, - gen_logger, wx], + wx], [App || App = {Name, _, _} <- Apps, not lists:member(Name, LibApps)]. From a59d5bb459b03be2050e7ed1240fa5ab11213900 Mon Sep 17 00:00:00 2001 From: Feng Date: Wed, 15 Feb 2017 22:05:28 +0800 Subject: [PATCH 011/125] Rename the emqttd_time APIs --- src/emqttd_alarm.erl | 4 ++-- src/emqttd_cli.erl | 4 ++-- src/emqttd_message.erl | 6 +++--- test/emqttd_lib_SUITE.erl | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/emqttd_alarm.erl b/src/emqttd_alarm.erl index 1ff4a5ae9..9e7404100 100644 --- a/src/emqttd_alarm.erl +++ b/src/emqttd_alarm.erl @@ -90,12 +90,12 @@ handle_event({set_alarm, Alarm = #mqtt_alarm{id = AlarmId, {severity, Severity}, {title, iolist_to_binary(Title)}, {summary, iolist_to_binary(Summary)}, - {ts, emqttd_time:now_to_secs(Timestamp)}]), + {ts, emqttd_time:now_secs(Timestamp)}]), emqttd:publish(alarm_msg(alert, AlarmId, Json)), {ok, [Alarm#mqtt_alarm{timestamp = Timestamp} | Alarms]}; handle_event({clear_alarm, AlarmId}, Alarms) -> - Json = mochijson2:encode([{id, AlarmId}, {ts, emqttd_time:now_to_secs()}]), + Json = mochijson2:encode([{id, AlarmId}, {ts, emqttd_time:now_secs()}]), emqttd:publish(alarm_msg(clear, AlarmId, Json)), {ok, lists:keydelete(AlarmId, 2, Alarms), hibernate}; diff --git a/src/emqttd_cli.erl b/src/emqttd_cli.erl index 085032b24..5e49d61c6 100644 --- a/src/emqttd_cli.erl +++ b/src/emqttd_cli.erl @@ -503,7 +503,7 @@ print(#mqtt_client{client_id = ClientId, clean_sess = CleanSess, username = User peername = Peername, connected_at = ConnectedAt}) -> ?PRINT("Client(~s, clean_sess=~s, username=~s, peername=~s, connected_at=~p)~n", [ClientId, CleanSess, Username, emqttd_net:format(Peername), - emqttd_time:now_to_secs(ConnectedAt)]); + emqttd_time:now_secs(ConnectedAt)]); %% print(#mqtt_topic{topic = Topic, flags = Flags}) -> %% ?PRINT("~s: ~s~n", [Topic, string:join([atom_to_list(F) || F <- Flags], ",")]); @@ -542,7 +542,7 @@ print(subscription, {Sub, Topic}) -> ?PRINT("~s -> ~s~n", [Sub, Topic]). format(created_at, Val) -> - emqttd_time:now_to_secs(Val); + emqttd_time:now_secs(Val); format(_, Val) -> Val. diff --git a/src/emqttd_message.erl b/src/emqttd_message.erl index ec4a02479..0fa3237c6 100644 --- a/src/emqttd_message.erl +++ b/src/emqttd_message.erl @@ -42,7 +42,7 @@ make(From, Qos, Topic, Payload) -> qos = ?QOS_I(Qos), topic = Topic, payload = Payload, - timestamp = emqttd_time:now_to_secs()}. + timestamp = emqttd_time:now_secs()}. %% @doc Message from Packet -spec(from_packet(mqtt_packet()) -> mqtt_message()). @@ -60,7 +60,7 @@ from_packet(#mqtt_packet{header = #mqtt_packet_header{type = ?PUBLISH, dup = Dup, topic = Topic, payload = Payload, - timestamp = emqttd_time:now_to_secs()}; + timestamp = emqttd_time:now_secs()}; from_packet(#mqtt_packet_connect{will_flag = false}) -> undefined; @@ -78,7 +78,7 @@ from_packet(#mqtt_packet_connect{client_id = ClientId, qos = Qos, dup = false, payload = Msg, - timestamp = emqttd_time:now_to_secs()}. + timestamp = emqttd_time:now_secs()}. from_packet(ClientId, Packet) -> Msg = from_packet(Packet), diff --git a/test/emqttd_lib_SUITE.erl b/test/emqttd_lib_SUITE.erl index e51e778f0..d4d44cb82 100644 --- a/test/emqttd_lib_SUITE.erl +++ b/test/emqttd_lib_SUITE.erl @@ -141,8 +141,8 @@ priority_queue_out2(_) -> time_now_to_(_) -> emqttd_time:seed(), - emqttd_time:now_to_secs(), - emqttd_time:now_to_ms(). + emqttd_time:now_secs(), + emqttd_time:now_ms(). %%-------------------------------------------------------------------- %% emqttd_node From 61a54b4b331bfe1d2ec15c3f7c22d37ae5daf1c7 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Thu, 16 Feb 2017 10:27:27 +0800 Subject: [PATCH 012/125] Bump version to 2.1 --- Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index a4e02ce45..4355cc413 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ PROJECT = emqttd PROJECT_DESCRIPTION = Erlang MQTT Broker -PROJECT_VERSION = 2.0.7 +PROJECT_VERSION = 2.1 DEPS = gproc lager esockd mochiweb @@ -24,9 +24,9 @@ TEST_ERLC_OPTS += +'{parse_transform, lager_transform}' EUNIT_OPTS = verbose # EUNIT_ERL_OPTS = -CT_SUITES = emqttd emqttd_access emqttd_lib emqttd_mod emqttd_net \ - emqttd_mqueue emqttd_protocol emqttd_topic emqttd_trie \ - emqttd_vm +CT_SUITES = emqttd emqttd_access emqttd_lib emqttd_inflight emqttd_mod \ + emqttd_net emqttd_mqueue emqttd_protocol emqttd_topic \ + emqttd_trie emqttd_vm CT_OPTS = -cover test/ct.cover.spec -erl_args -name emqttd_ct@127.0.0.1 From 08100525a5a3d31fd36f7905859d721bc3dbc0a9 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Thu, 16 Feb 2017 10:44:00 +0800 Subject: [PATCH 013/125] Add inflight module and test suite --- src/{emqttd_hook.erl => emqttd_hooks.erl} | 0 src/emqttd_inflight.erl | 94 +++++++++++++++++++++++ src/emqttd_opts.erl | 35 --------- test/emqttd_inflight_SUITE.erl | 50 ++++++++++++ 4 files changed, 144 insertions(+), 35 deletions(-) rename src/{emqttd_hook.erl => emqttd_hooks.erl} (100%) create mode 100644 src/emqttd_inflight.erl delete mode 100644 src/emqttd_opts.erl create mode 100644 test/emqttd_inflight_SUITE.erl diff --git a/src/emqttd_hook.erl b/src/emqttd_hooks.erl similarity index 100% rename from src/emqttd_hook.erl rename to src/emqttd_hooks.erl diff --git a/src/emqttd_inflight.erl b/src/emqttd_inflight.erl new file mode 100644 index 000000000..bb9af390b --- /dev/null +++ b/src/emqttd_inflight.erl @@ -0,0 +1,94 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) +%% +%% 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. +%%-------------------------------------------------------------------- + +%% @doc Inflight Window that wraps the gb_trees. + +-module(emqttd_inflight). + +-author("Feng Lee "). + +-export([new/1, contain/2, lookup/2, insert/3, update/3, delete/2, values/1, + to_list/1, size/1, max_size/1, is_full/1, is_empty/1, window/1]). + +-type(inflight() :: {?MODULE, list()}). + +-export_type([inflight/0]). + +-spec(new(non_neg_integer()) -> inflight()). +new(MaxSize) when MaxSize >= 0 -> + {?MODULE, [MaxSize, gb_trees:empty()]}. + +-spec(contain(Key :: any(), inflight()) -> boolean()). +contain(Key, {?MODULE, [_MaxSize, Tree]}) -> + gb_trees:is_defined(Key, Tree). + +-spec(lookup(Key :: any(), inflight()) -> any()). +lookup(Key, {?MODULE, [_MaxSize, Tree]}) -> + gb_trees:get(Key, Tree). + +-spec(insert(Key :: any(), Value :: any(), inflight()) -> inflight()). +insert(Key, Value, {?MODULE, [MaxSize, Tree]}) -> + {?MODULE, [MaxSize, gb_trees:insert(Key, Value, Tree)]}. + +-spec(delete(Key :: any(), inflight()) -> inflight()). +delete(Key, {?MODULE, [MaxSize, Tree]}) -> + {?MODULE, [MaxSize, gb_trees:delete(Key, Tree)]}. + +-spec(update(Key :: any(), Val :: any(), inflight()) -> inflight()). +update(Key, Val, {?MODULE, [MaxSize, Tree]}) -> + {?MODULE, [MaxSize, gb_trees:update(Key, Val, Tree)]}. + +-spec(is_full(inflight()) -> boolean()). +is_full({?MODULE, [0, _Tree]}) -> + false; +is_full({?MODULE, [MaxSize, Tree]}) -> + MaxSize =< gb_trees:size(Tree). + +-spec(is_empty(inflight()) -> boolean()). +is_empty({?MODULE, [_MaxSize, Tree]}) -> + gb_trees:is_empty(Tree). + +-spec(smallest(inflight()) -> {K :: any(), V :: any()}). +smallest({?MODULE, [_MaxSize, Tree]}) -> + gb_trees:smallest(Tree). + +-spec(largest(inflight()) -> {K :: any(), V :: any()}). +largest({?MODULE, [_MaxSize, Tree]}) -> + gb_trees:largest(Tree). + +-spec(values(inflight()) -> list()). +values({?MODULE, [_MaxSize, Tree]}) -> + gb_trees:values(Tree). + +-spec(to_list(inflight()) -> list({K :: any(), V :: any()})). +to_list({?MODULE, [_MaxSize, Tree]}) -> + gb_trees:to_list(Tree). + +-spec(window(inflight()) -> list()). +window(Inflight = {?MODULE, [_MaxSize, Tree]}) -> + case gb_trees:is_empty(Tree) of + true -> []; + false -> [Key || {Key, _Val} <- [smallest(Inflight), largest(Inflight)]] + end. + +-spec(size(inflight()) -> non_neg_integer()). +size({?MODULE, [_MaxSize, Tree]}) -> + gb_trees:size(Tree). + +-spec(max_size(inflight()) -> non_neg_integer()). +max_size({?MODULE, [MaxSize, _Tree]}) -> + MaxSize. + diff --git a/src/emqttd_opts.erl b/src/emqttd_opts.erl deleted file mode 100644 index 8f5f3fea5..000000000 --- a/src/emqttd_opts.erl +++ /dev/null @@ -1,35 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . -%% -%% 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(emqttd_opts). - --export([merge/2]). - -%% @doc Merge Options -merge(Defaults, Options) -> - lists:foldl( - fun({Opt, Val}, Acc) -> - case lists:keymember(Opt, 1, Acc) of - true -> lists:keyreplace(Opt, 1, Acc, {Opt, Val}); - false -> [{Opt, Val}|Acc] - end; - (Opt, Acc) -> - case lists:member(Opt, Acc) of - true -> Acc; - false -> [Opt | Acc] - end - end, Defaults, Options). - diff --git a/test/emqttd_inflight_SUITE.erl b/test/emqttd_inflight_SUITE.erl new file mode 100644 index 000000000..5a87d056d --- /dev/null +++ b/test/emqttd_inflight_SUITE.erl @@ -0,0 +1,50 @@ +%% +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) +%% + +-module(emqttd_inflight_SUITE). + +-author("Feng Lee "). + +-include_lib("eunit/include/eunit.hrl"). + +%% CT +-compile(export_all). + +all() -> [t_contain, t_lookup, t_insert, t_update, t_delete, t_window, + t_is_full, t_is_empty]. + +t_contain(_) -> + Inflight = emqttd_inflight:new(0), + ?assertNot(Inflight:contain(k)), + Inflight1 = Inflight:insert(k, v), + ?assert(Inflight1:contain(k)). + +t_lookup(_) -> + Inflight = (emqttd_inflight:new(0)):insert(k, v), + ?assertEqual(v, Inflight:lookup(k)). + +t_insert(_) -> + Inflight = ((emqttd_inflight:new(0)):insert(k1, v1)):insert(k2, v2), + ?assertEqual(v2, Inflight:lookup(k2)). + +t_update(_) -> + Inflight = ((emqttd_inflight:new(0)):insert(k, v1)):update(k, v2), + ?assertEqual(v2, Inflight:lookup(k)). + +t_delete(_) -> + Inflight = ((emqttd_inflight:new(0)):insert(k, v1)):delete(k), + ?assert(Inflight:is_empty()). + +t_window(_) -> + ?assertEqual([], (emqttd_inflight:new(10)):window()), + Inflight = ((emqttd_inflight:new(0)):insert(1, 1)):insert(2, 2), + ?assertEqual([1, 2], Inflight:window()). + +t_is_full(_) -> + Inflight = ((emqttd_inflight:new(1)):insert(k, v1)), + ?assert(Inflight:is_full()). + +t_is_empty(_) -> + Inflight = ((emqttd_inflight:new(1)):insert(k, v1)), + ?assertNot(Inflight:is_empty()). From 0bc071d792caaefce2aa2621d039736d4da0addc Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Thu, 16 Feb 2017 10:48:31 +0800 Subject: [PATCH 014/125] Add emqttd_misc module --- src/emqttd_misc.erl | 63 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 src/emqttd_misc.erl diff --git a/src/emqttd_misc.erl b/src/emqttd_misc.erl new file mode 100644 index 000000000..8111264cc --- /dev/null +++ b/src/emqttd_misc.erl @@ -0,0 +1,63 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) +%% +%% 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(emqttd_misc). + +-author("Feng Lee "). + +-export([merge_opts/2, start_timer/2, start_timer/3, cancel_timer/1, + proc_stats/0, proc_stats/1]). + +%% @doc Merge Options +merge_opts(Defaults, Options) -> + lists:foldl( + fun({Opt, Val}, Acc) -> + case lists:keymember(Opt, 1, Acc) of + true -> lists:keyreplace(Opt, 1, Acc, {Opt, Val}); + false -> [{Opt, Val}|Acc] + end; + (Opt, Acc) -> + case lists:member(Opt, Acc) of + true -> Acc; + false -> [Opt | Acc] + end + end, Defaults, Options). + +-spec(start_timer(integer(), term()) -> reference()). +start_timer(Interval, Msg) -> + start_timer(Interval, self(), Msg). + +-spec(start_timer(integer(), pid() | atom(), term()) -> reference()). +start_timer(Interval, Dest, Msg) -> + erlang:start_timer(Interval, Dest, Msg). + +-spec(cancel_timer(undefined | reference()) -> ok). +cancel_timer(undefined) -> + ok; +cancel_timer(Timer) -> + case catch erlang:cancel_timer(Timer) of + false -> receive {timeout, Timer, _} -> ok after 0 -> ok end; + _ -> ok + end. + +proc_stats() -> + proc_stats(self()). + +proc_stats(Pid) -> + Stats = process_info(Pid, [message_queue_len, heap_size, reductions]), + {value, {_, V}, Stats1} = lists:keytake(message_queue_len, 1, Stats), + [{mailbox_len, V} | Stats1]. + From a52754df5629e6a5b8a5a6e3824275193d945d60 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Thu, 16 Feb 2017 10:51:03 +0800 Subject: [PATCH 015/125] Add 'syslog' config and update client, session config --- etc/emq.conf | 79 +++++++++++++++------- priv/emq.schema | 174 +++++++++++++++++++++++++++++++++--------------- 2 files changed, 176 insertions(+), 77 deletions(-) diff --git a/etc/emq.conf b/etc/emq.conf index 7238f3395..e30ddfa26 100644 --- a/etc/emq.conf +++ b/etc/emq.conf @@ -1,3 +1,8 @@ + +##=================================================================== +## EMQ Configuration R2.1 +##=================================================================== + ##-------------------------------------------------------------------- ## Node Args ##-------------------------------------------------------------------- @@ -45,16 +50,25 @@ node.crash_dump = {{ platform_log_dir }}/crash.dump node.dist_net_ticktime = 60 ## Distributed node port range -## node.dist_listen_min = 6000 -## node.dist_listen_max = 6999 +## node.dist_listen_min = 6369 +## node.dist_listen_max = 6369 ##-------------------------------------------------------------------- ## Log ##-------------------------------------------------------------------- +## Set the log dir +log.dir = {{ platform_log_dir }} + ## Console log. Enum: off, file, console, both log.console = console +## Syslog. Enum: on, off +log.syslog = on + +## syslog level. Enum: debug, info, notice, warning, error, critical, alert, emergency +log.syslog.level = error + ## Console log level. Enum: debug, info, notice, warning, error, critical, alert, emergency log.console.level = error @@ -70,18 +84,9 @@ log.crash = on log.crash.file = {{ platform_log_dir }}/crash.log ##-------------------------------------------------------------------- -## MQTT Protocol +## Allow Anonymous and Default ACL ##-------------------------------------------------------------------- -## Max ClientId Length Allowed. -mqtt.max_clientid_len = 1024 - -## Max Packet Size Allowed, 64K by default. -mqtt.max_packet_size = 64KB - -## Client Idle Timeout (Second) -mqtt.client_idle_timeout = 30 - ## Allow Anonymous authentication mqtt.allow_anonymous = true @@ -91,25 +96,48 @@ mqtt.acl_file = {{ platform_etc_dir }}/acl.conf ## Cache ACL for PUBLISH mqtt.cache_acl = true +##-------------------------------------------------------------------- +## MQTT Protocol +##-------------------------------------------------------------------- + +## Max ClientId Length Allowed. +mqtt.max_clientid_len = 1024 + +## Max Packet Size Allowed, 64K by default. +mqtt.max_packet_size = 64KB + +##-------------------------------------------------------------------- +## MQTT Client +##-------------------------------------------------------------------- + +## Client Idle Timeout (Second) +mqtt.client.idle_timeout = 30s + +## Enable client Stats: seconds or off +mqtt.client.enable_stats = off + ##-------------------------------------------------------------------- ## MQTT Session ##-------------------------------------------------------------------- +## Upgrade QoS? +mqtt.session.upgrade_qos = off + ## Max number of QoS 1 and 2 messages that can be “inflight” at one time. ## 0 means no limit -mqtt.session.max_inflight = 100 +mqtt.session.max_inflight = 32 -## Retry interval for redelivering QoS1/2 messages. -mqtt.session.retry_interval = 60 - -## Awaiting PUBREL Timeout -mqtt.session.await_rel_timeout = 20 +## Retry Interval for redelivering QoS1/2 messages. +mqtt.session.retry_interval = 20s ## Max Packets that Awaiting PUBREL, 0 means no limit -mqtt.session.max_awaiting_rel = 0 +mqtt.session.max_awaiting_rel = 100 -## Statistics Collection Interval(seconds) -mqtt.session.collect_interval = 0 +## Awaiting PUBREL Timeout +mqtt.session.await_rel_timeout = 20s + +## Enable Statistics at the Interval(seconds) +mqtt.session.enable_stats = off ## Expired after 1 day: ## w - week @@ -117,7 +145,7 @@ mqtt.session.collect_interval = 0 ## h - hour ## m - minute ## s - second -mqtt.session.expired_after = 1d +mqtt.session.expiry_interval = 2h ##-------------------------------------------------------------------- ## MQTT Queue @@ -212,9 +240,10 @@ mqtt.listener.ssl.max_clients = 512 ## Rate Limit. Format is 'burst,rate', Unit is KB/Sec ## mqtt.listener.ssl.rate_limit = 100,10 -## Configuring SSL Options -## See http://erlang.org/doc/man/ssl.html -mqtt.listener.ssl.handshake_timeout = 15 +## Configuring SSL Options. See http://erlang.org/doc/man/ssl.html +### TLS only for POODLE attack +mqtt.listener.ssl.tls_versions = tlsv1.2,tlsv1.1,tlsv1 +mqtt.listener.ssl.handshake_timeout = 15s mqtt.listener.ssl.keyfile = {{ platform_etc_dir }}/certs/key.pem mqtt.listener.ssl.certfile = {{ platform_etc_dir }}/certs/cert.pem ## mqtt.listener.ssl.cacertfile = {{ platform_etc_dir }}/certs/cacert.pem diff --git a/priv/emq.schema b/priv/emq.schema index 1722522c4..156bc4874 100644 --- a/priv/emq.schema +++ b/priv/emq.schema @@ -148,8 +148,13 @@ end}. %% Log %%-------------------------------------------------------------------- +{mapping, "log.dir", "lager.log_dir", [ + {default, "log"}, + {datatype, string} +]}. + {mapping, "log.console", "lager.handlers", [ - {default, file }, + {default, file}, {datatype, {enum, [off, file, console, both]}} ]}. @@ -168,6 +173,26 @@ end}. {datatype, file} ]}. +{mapping, "log.syslog", "lager.handlers", [ + {default, off}, + {datatype, flag} +]}. + +{mapping, "log.syslog.identity", "lager.handlers", [ + {default, "emq"}, + {datatype, string} +]}. + +{mapping, "log.syslog.facility", "lager.handlers", [ + {default, local0}, + {datatype, {enum, [daemon, local0, local1, local2, local3, local4, local5, local6, local7]}} +]}. + +{mapping, "log.syslog.level", "lager.handlers", [ + {default, err}, + {datatype, {enum, [debug, info, notice, warning, error, critical, alert, emergency]}} +]}. + {mapping, "log.error.redirect", "lager.error_logger_redirect", [ {default, on}, {datatype, flag}, @@ -209,7 +234,16 @@ end}. both -> [ConsoleHandler, ConsoleFileHandler]; _ -> [] end, - ConsoleHandlers ++ ErrorHandler + + SyslogHandler = case cuttlefish:conf_get("log.syslog", Conf) of + false -> []; + true -> [{lager_syslog_backend, + [cuttlefish:conf_get("log.syslog.identity", Conf), + cuttlefish:conf_get("log.syslog.facility", Conf), + cuttlefish:conf_get("log.syslog.level", Conf)]}] + end, + + ConsoleHandlers ++ ErrorHandler ++ SyslogHandler end }. @@ -240,33 +274,9 @@ end}. ]}. %%-------------------------------------------------------------------- -%% MQTT Protocol +%% Allow Anonymous and Default ACL %%-------------------------------------------------------------------- -%% @doc Set the Max ClientId Length Allowed. -{mapping, "mqtt.max_clientid_len", "emqttd.protocol", [ - {default, 1024}, - {datatype, integer} -]}. - -%% @doc Max Packet Size Allowed, 64K by default. -{mapping, "mqtt.max_packet_size", "emqttd.protocol", [ - {default, "64KB"}, - {datatype, bytesize} -]}. - -%% @doc Client Idle Timeout. -{mapping, "mqtt.client_idle_timeout", "emqttd.protocol", [ - {default, 30}, - {datatype, integer} -]}. - -{translation, "emqttd.protocol", fun(Conf) -> - [{max_clientid_len, cuttlefish:conf_get("mqtt.max_clientid_len", Conf)}, - {max_packet_size, cuttlefish:conf_get("mqtt.max_packet_size", Conf)}, - {client_idle_timeout, cuttlefish:conf_get("mqtt.client_idle_timeout", Conf)}] -end}. - %% @doc Allow Anonymous {mapping, "mqtt.allow_anonymous", "emqttd.allow_anonymous", [ {default, false}, @@ -285,10 +295,58 @@ end}. {datatype, {enum, [true, false]}} ]}. +%%-------------------------------------------------------------------- +%% MQTT Protocol +%%-------------------------------------------------------------------- + +%% @doc Set the Max ClientId Length Allowed. +{mapping, "mqtt.max_clientid_len", "emqttd.protocol", [ + {default, 1024}, + {datatype, integer} +]}. + +%% @doc Max Packet Size Allowed, 64K by default. +{mapping, "mqtt.max_packet_size", "emqttd.protocol", [ + {default, "64KB"}, + {datatype, bytesize} +]}. + +{translation, "emqttd.protocol", fun(Conf) -> + [{max_clientid_len, cuttlefish:conf_get("mqtt.max_clientid_len", Conf)}, + {max_packet_size, cuttlefish:conf_get("mqtt.max_packet_size", Conf)}] +end}. + +%%-------------------------------------------------------------------- +%% MQTT Client +%%-------------------------------------------------------------------- + +%% @doc Client Idle Timeout. +{mapping, "mqtt.client.idle_timeout", "emqttd.client", [ + {default, "30s"}, + {datatype, {duration, ms}} +]}. + +%% @doc Enable Stats of Client. +{mapping, "mqtt.client.enable_stats", "emqttd.client", [ + {default, off}, + {datatype, [{duration, ms}, flag]} +]}. + +%% @doc Client +{translation, "emqttd.client", fun(Conf) -> + [{client_idle_timeout, cuttlefish:conf_get("mqtt.client.idle_timeout", Conf)}, + {client_enable_stats, cuttlefish:conf_get("mqtt.client.enable_stats", Conf)}] +end}. + %%-------------------------------------------------------------------- %% MQTT Session %%-------------------------------------------------------------------- +%% @doc Upgrade QoS? +{mapping, "mqtt.session.upgrade_qos", "emqttd.session", [ + {default, off}, + {datatype, flag} +]}. %% @doc Max number of QoS 1 and 2 messages that can be “inflight” at one time. %% 0 means no limit {mapping, "mqtt.session.max_inflight", "emqttd.session", [ @@ -296,17 +354,10 @@ end}. {datatype, integer} ]}. - %% @doc Retry interval for redelivering QoS1/2 messages. {mapping, "mqtt.session.retry_interval", "emqttd.session", [ - {default, 60}, - {datatype, integer} -]}. - -%% @doc Awaiting PUBREL Timeout -{mapping, "mqtt.session.await_rel_timeout", "emqttd.session", [ - {default, 30}, - {datatype, integer} + {default, "20s"}, + {datatype, {duration, ms}} ]}. %% @doc Max Packets that Awaiting PUBREL, 0 means no limit @@ -315,25 +366,32 @@ end}. {datatype, integer} ]}. -%% @doc Statistics Collection Interval(seconds) -{mapping, "mqtt.session.collect_interval", "emqttd.session", [ - {default, 0}, - {datatype, integer} +%% @doc Awaiting PUBREL Timeout +{mapping, "mqtt.session.await_rel_timeout", "emqttd.session", [ + {default, "20s"}, + {datatype, {duration, ms}} ]}. -%% @doc Session expired after... -{mapping, "mqtt.session.expired_after", "emqttd.session", [ - {default, "2d"}, - {datatype, {duration, s}} +%% @doc Enable Stats +{mapping, "mqtt.session.enable_stats", "emqttd.session", [ + {default, off}, + {datatype, [{duration, ms}, flag]} +]}. + +%% @doc Session Expiry Interval +{mapping, "mqtt.session.expiry_interval", "emqttd.session", [ + {default, "2h"}, + {datatype, {duration, ms}} ]}. {translation, "emqttd.session", fun(Conf) -> - [{max_inflight, cuttlefish:conf_get("mqtt.session.max_inflight", Conf)}, - {retry_interval, cuttlefish:conf_get("mqtt.session.retry_interval", Conf)}, + [{upgrade_qos, cuttlefish:conf_get("mqtt.session.upgrade_qos", Conf)}, + {max_inflight, cuttlefish:conf_get("mqtt.session.max_inflight", Conf)}, + {retry_interval, cuttlefish:conf_get("mqtt.session.retry_interval", Conf)}, + {max_awaiting_rel, cuttlefish:conf_get("mqtt.session.max_awaiting_rel", Conf)}, {await_rel_timeout, cuttlefish:conf_get("mqtt.session.await_rel_timeout", Conf)}, - {max_awaiting_rel, cuttlefish:conf_get("mqtt.session.max_awaiting_rel", Conf)}, - {collect_interval, cuttlefish:conf_get("mqtt.session.collect_interval", Conf)}, - {expired_after, cuttlefish:conf_get("mqtt.session.expired_after", Conf)}] + {enable_stats, cuttlefish:conf_get("mqtt.session.enable_stats", Conf)}, + {expiry_interval, cuttlefish:conf_get("mqtt.session.expiry_interval", Conf)}] end}. %%-------------------------------------------------------------------- @@ -528,9 +586,13 @@ end}. {datatype, string} ]}. +{mapping, "mqtt.listener.ssl.tls_versions", "emqttd.listeners", [ + {datatype, string} +]}. + {mapping, "mqtt.listener.ssl.handshake_timeout", "emqttd.listeners", [ - {default, 15}, - {datatype, integer} + {default, "15s"}, + {datatype, {duration, ms}} ]}. {mapping, "mqtt.listener.ssl.keyfile", "emqttd.listeners", [ @@ -622,8 +684,16 @@ end}. {buffer, cuttlefish:conf_get(Prefix ++ ".buffer", Conf, undefined)}, {nodelay, cuttlefish:conf_get(Prefix ++ ".nodelay", Conf, true)}]) end, + + SplitFun = fun(undefined) -> undefined; (S) -> string:tokens(S, ",") end, + SslOpts = fun(Prefix) -> - Filter([{handshake_timeout, cuttlefish:conf_get(Prefix ++ ".handshake_timeout", Conf) * 1000}, + Versions = case SplitFun(cuttlefish:conf_get(Prefix ++ ".tls_versions", Conf, undefined)) of + undefined -> undefined; + L -> [list_to_atom(V) || V <- L] + end, + Filter([{versions, Versions}, + {handshake_timeout, cuttlefish:conf_get(Prefix ++ ".handshake_timeout", Conf), undefined}, {keyfile, cuttlefish:conf_get(Prefix ++ ".keyfile", Conf, undefined)}, {certfile, cuttlefish:conf_get(Prefix ++ ".certfile", Conf, undefined)}, {cacertfile, cuttlefish:conf_get(Prefix ++ ".cacertfile", Conf, undefined)}, From 4df2a71c27cbf8881d8759d980ca002a3678aa7e Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Thu, 16 Feb 2017 10:53:51 +0800 Subject: [PATCH 016/125] Update copyright, format record, add 'AUTH' packet type for MQTT 5.0 --- include/emqttd.hrl | 148 +++++++++++++++-------------- include/emqttd_cli.hrl | 2 +- include/emqttd_internal.hrl | 2 +- include/emqttd_protocol.hrl | 183 ++++++++++++++++++++---------------- include/emqttd_trie.hrl | 30 +++--- 5 files changed, 198 insertions(+), 167 deletions(-) diff --git a/include/emqttd.hrl b/include/emqttd.hrl index 53350ed6d..0d7d4c134 100644 --- a/include/emqttd.hrl +++ b/include/emqttd.hrl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ %% Banner %%-------------------------------------------------------------------- --define(COPYRIGHT, "Copyright (C) 2012-2017, Feng Lee "). +-define(COPYRIGHT, "Copyright (c) 2013-2017 EMQ Enterprise, Inc."). -define(LICENSE_MESSAGE, "Licensed under the Apache License, Version 2.0"). @@ -48,21 +48,21 @@ %% MQTT Topic %%-------------------------------------------------------------------- --record(mqtt_topic, { - topic :: binary(), - flags = [] :: [retained | static] -}). +-record(mqtt_topic, + { topic :: binary(), + flags = [] :: [retained | static] + }). -type(mqtt_topic() :: #mqtt_topic{}). %%-------------------------------------------------------------------- %% MQTT Subscription %%-------------------------------------------------------------------- --record(mqtt_subscription, { - subid :: binary() | atom(), - topic :: binary(), - qos :: 0 | 1 | 2 -}). +-record(mqtt_subscription, + { subid :: binary() | atom(), + topic :: binary(), + qos :: 0 | 1 | 2 + }). -type(mqtt_subscription() :: #mqtt_subscription{}). @@ -73,18 +73,18 @@ -type(ws_header_key() :: atom() | binary() | string()). -type(ws_header_val() :: atom() | binary() | string() | integer()). --record(mqtt_client, { - client_id :: binary() | undefined, - client_pid :: pid(), - username :: binary() | undefined, - peername :: {inet:ip_address(), integer()}, - clean_sess :: boolean(), - proto_ver :: 3 | 4, - keepalive = 0, - will_topic :: undefined | binary(), - ws_initial_headers :: list({ws_header_key(), ws_header_val()}), - connected_at :: erlang:timestamp() -}). +-record(mqtt_client, + { client_id :: binary() | undefined, + client_pid :: pid(), + username :: binary() | undefined, + peername :: {inet:ip_address(), inet:port_number()}, + clean_sess :: boolean(), + proto_ver :: 3 | 4, + keepalive = 0, + will_topic :: undefined | binary(), + ws_initial_headers :: list({ws_header_key(), ws_header_val()}), + connected_at :: erlang:timestamp() + }). -type(mqtt_client() :: #mqtt_client{}). @@ -92,33 +92,46 @@ %% MQTT Session %%-------------------------------------------------------------------- --record(mqtt_session, { - client_id :: binary(), - sess_pid :: pid(), - persistent :: boolean() -}). +-record(mqtt_session, + { client_id :: binary(), + sess_pid :: pid(), + clean_sess :: boolean() + }). -type(mqtt_session() :: #mqtt_session{}). %%-------------------------------------------------------------------- %% MQTT Message %%-------------------------------------------------------------------- + -type(mqtt_msgid() :: binary() | undefined). + -type(mqtt_pktid() :: 1..16#ffff | undefined). --record(mqtt_message, { - id :: mqtt_msgid(), %% Global unique message ID - pktid :: mqtt_pktid(), %% PacketId - from :: {binary(), undefined | binary()}, %% ClientId and Username - topic :: binary(), %% Topic that the message is published to - qos = 0 :: 0 | 1 | 2, %% Message QoS - flags = [] :: [retain | dup | sys], %% Message Flags - retain = false :: boolean(), %% Retain flag - dup = false :: boolean(), %% Dup flag - sys = false :: boolean(), %% $SYS flag - headers = [] :: list(), - payload :: binary(), %% Payload - timestamp :: pos_integer() %% os:timestamp to seconds +-record(mqtt_message, + { %% Global unique message ID + id :: mqtt_msgid(), + %% PacketId + pktid :: mqtt_pktid(), + %% ClientId and Username + from :: {binary(), undefined | binary()}, + %% Topic that the message is published to + topic :: binary(), + %% Message QoS + qos = 0 :: 0 | 1 | 2, + %% Message Flags + flags = [] :: [retain | dup | sys], + %% Retain flag + retain = false :: boolean(), + %% Dup flag + dup = false :: boolean(), + %% $SYS flag + sys = false :: boolean(), + headers = [] :: list(), + %% Payload + payload :: binary(), + %% Timestamp + timestamp :: erlang:timestamp() }). -type(mqtt_message() :: #mqtt_message{}). @@ -126,46 +139,45 @@ %%-------------------------------------------------------------------- %% MQTT Delivery %%-------------------------------------------------------------------- --record(mqtt_delivery, { - sender :: pid(), %% Pid of the sender/publisher - message :: mqtt_message(), %% Message - flows :: list() -}). + +-record(mqtt_delivery, + { sender :: pid(), %% Pid of the sender/publisher + message :: mqtt_message(), %% Message + flows :: list() + }). -type(mqtt_delivery() :: #mqtt_delivery{}). %%-------------------------------------------------------------------- %% MQTT Route %%-------------------------------------------------------------------- --record(mqtt_route, { - topic :: binary(), - node :: node() -}). + +-record(mqtt_route, + { topic :: binary(), + node :: node() + }). -type(mqtt_route() :: #mqtt_route{}). %%-------------------------------------------------------------------- %% MQTT Alarm %%-------------------------------------------------------------------- --record(mqtt_alarm, { - id :: binary(), - severity :: warning | error | critical, - title :: iolist() | binary(), - summary :: iolist() | binary(), - timestamp :: erlang:timestamp() %% Timestamp -}). + +-record(mqtt_alarm, + { id :: binary(), + severity :: warning | error | critical, + title :: iolist() | binary(), + summary :: iolist() | binary(), + timestamp :: erlang:timestamp() %% Timestamp + }). -type(mqtt_alarm() :: #mqtt_alarm{}). %%-------------------------------------------------------------------- %% MQTT Plugin %%-------------------------------------------------------------------- --record(mqtt_plugin, { - name, - version, - descr, - active = false -}). + +-record(mqtt_plugin, { name, version, descr, active = false }). -type(mqtt_plugin() :: #mqtt_plugin{}). @@ -173,14 +185,8 @@ %% MQTT CLI Command %% For example: 'broker metrics' %%-------------------------------------------------------------------- --record(mqtt_cli, { - name, - action, - args = [], - opts = [], - usage, - descr -}). + +-record(mqtt_cli, { name, action, args = [], opts = [], usage, descr }). -type(mqtt_cli() :: #mqtt_cli{}). diff --git a/include/emqttd_cli.hrl b/include/emqttd_cli.hrl index 461306747..bda88d801 100644 --- a/include/emqttd_cli.hrl +++ b/include/emqttd_cli.hrl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/include/emqttd_internal.hrl b/include/emqttd_internal.hrl index 06102e26a..ec5fb3e73 100644 --- a/include/emqttd_internal.hrl +++ b/include/emqttd_internal.hrl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/include/emqttd_protocol.hrl b/include/emqttd_protocol.hrl index 9ed3a993b..ab0650ead 100644 --- a/include/emqttd_protocol.hrl +++ b/include/emqttd_protocol.hrl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -14,23 +14,32 @@ %% limitations under the License. %%-------------------------------------------------------------------- -%% MQTT Protocol Header +%%-------------------------------------------------------------------- +%% MQTT SockOpts +%%-------------------------------------------------------------------- + +-define(MQTT_SOCKOPTS, [binary, {packet, raw}, {reuseaddr, true}, + {backlog, 512}, {nodelay, true}]). %%-------------------------------------------------------------------- %% MQTT Protocol Version and Levels %%-------------------------------------------------------------------- --define(MQTT_PROTO_V31, 3). --define(MQTT_PROTO_V311, 4). + +-define(MQTT_PROTO_V3, 3). +-define(MQTT_PROTO_V4, 4). +-define(MQTT_PROTO_V5, 5). -define(PROTOCOL_NAMES, [ - {?MQTT_PROTO_V31, <<"MQIsdp">>}, - {?MQTT_PROTO_V311, <<"MQTT">>}]). + {?MQTT_PROTO_V3, <<"MQIsdp">>}, + {?MQTT_PROTO_V4, <<"MQTT">>}, + {?MQTT_PROTO_V5, <<"MQTT">>}]). --type(mqtt_vsn() :: ?MQTT_PROTO_V31 | ?MQTT_PROTO_V311). +-type(mqtt_vsn() :: ?MQTT_PROTO_V3 | ?MQTT_PROTO_V4 | ?MQTT_PROTO_V5). %%-------------------------------------------------------------------- -%% MQTT QoS +%% MQTT QoS Level %%-------------------------------------------------------------------- + -define(QOS_0, 0). %% At most once -define(QOS_1, 1). %% At least once -define(QOS_2, 2). %% Exactly once @@ -63,28 +72,30 @@ end). %%-------------------------------------------------------------------- -%% Max ClientId Length. Why 1024? NiDongDe... +%% Max ClientId Length. Why 1024? %%-------------------------------------------------------------------- + -define(MAX_CLIENTID_LEN, 1024). %%-------------------------------------------------------------------- %% MQTT Control Packet Types %%-------------------------------------------------------------------- --define(RESERVED, 0). %% Reserved --define(CONNECT, 1). %% Client request to connect to Server --define(CONNACK, 2). %% Server to Client: Connect acknowledgment --define(PUBLISH, 3). %% Publish message --define(PUBACK, 4). %% Publish acknowledgment --define(PUBREC, 5). %% Publish received (assured delivery part 1) --define(PUBREL, 6). %% Publish release (assured delivery part 2) --define(PUBCOMP, 7). %% Publish complete (assured delivery part 3) --define(SUBSCRIBE, 8). %% Client subscribe request --define(SUBACK, 9). %% Server Subscribe acknowledgment --define(UNSUBSCRIBE, 10). %% Unsubscribe request --define(UNSUBACK, 11). %% Unsubscribe acknowledgment --define(PINGREQ, 12). %% PING request --define(PINGRESP, 13). %% PING response --define(DISCONNECT, 14). %% Client is disconnecting +-define(RESERVED, 0). %% Reserved +-define(CONNECT, 1). %% Client request to connect to Server +-define(CONNACK, 2). %% Server to Client: Connect acknowledgment +-define(PUBLISH, 3). %% Publish message +-define(PUBACK, 4). %% Publish acknowledgment +-define(PUBREC, 5). %% Publish received (assured delivery part 1) +-define(PUBREL, 6). %% Publish release (assured delivery part 2) +-define(PUBCOMP, 7). %% Publish complete (assured delivery part 3) +-define(SUBSCRIBE, 8). %% Client subscribe request +-define(SUBACK, 9). %% Server Subscribe acknowledgment +-define(UNSUBSCRIBE, 10). %% Unsubscribe request +-define(UNSUBACK, 11). %% Unsubscribe acknowledgment +-define(PINGREQ, 12). %% PING request +-define(PINGRESP, 13). %% PING response +-define(DISCONNECT, 14). %% Client is disconnecting +-define(AUTH, 15). %% Authentication exchange -define(TYPE_NAMES, [ 'CONNECT', @@ -100,25 +111,28 @@ 'UNSUBACK', 'PINGREQ', 'PINGRESP', - 'DISCONNECT']). + 'DISCONNECT', + 'AUTH']). -type(mqtt_packet_type() :: ?RESERVED..?DISCONNECT). %%-------------------------------------------------------------------- %% MQTT Connect Return Codes %%-------------------------------------------------------------------- --define(CONNACK_ACCEPT, 0). %% Connection accepted --define(CONNACK_PROTO_VER, 1). %% Unacceptable protocol version --define(CONNACK_INVALID_ID, 2). %% Client Identifier is correct UTF-8 but not allowed by the Server --define(CONNACK_SERVER, 3). %% Server unavailable --define(CONNACK_CREDENTIALS, 4). %% Username or password is malformed --define(CONNACK_AUTH, 5). %% Client is not authorized to connect + +-define(CONNACK_ACCEPT, 0). %% Connection accepted +-define(CONNACK_PROTO_VER, 1). %% Unacceptable protocol version +-define(CONNACK_INVALID_ID, 2). %% Client Identifier is correct UTF-8 but not allowed by the Server +-define(CONNACK_SERVER, 3). %% Server unavailable +-define(CONNACK_CREDENTIALS, 4). %% Username or password is malformed +-define(CONNACK_AUTH, 5). %% Client is not authorized to connect -type(mqtt_connack() :: ?CONNACK_ACCEPT..?CONNACK_AUTH). %%-------------------------------------------------------------------- %% MQTT Parser and Serializer %%-------------------------------------------------------------------- + -define(MAX_LEN, 16#fffffff). -define(HIGHBIT, 2#10000000). -define(LOWBITS, 2#01111111). @@ -126,76 +140,87 @@ %%-------------------------------------------------------------------- %% MQTT Packet Fixed Header %%-------------------------------------------------------------------- + -record(mqtt_packet_header, { - type = ?RESERVED :: mqtt_packet_type(), - dup = false :: boolean(), - qos = ?QOS_0 :: mqtt_qos(), - retain = false :: boolean()}). + type = ?RESERVED :: mqtt_packet_type(), + dup = false :: boolean(), + qos = ?QOS_0 :: mqtt_qos(), + retain = false :: boolean()}). %%-------------------------------------------------------------------- %% MQTT Packets %%-------------------------------------------------------------------- --type(mqtt_client_id() :: binary()). --type(mqtt_username() :: binary() | undefined). --type(mqtt_packet_id() :: 1..16#ffff | undefined). --record(mqtt_packet_connect, { - client_id = <<>> :: mqtt_client_id(), - proto_ver = ?MQTT_PROTO_V311 :: mqtt_vsn(), - proto_name = <<"MQTT">> :: binary(), - will_retain = false :: boolean(), - will_qos = ?QOS_0 :: mqtt_qos(), - will_flag = false :: boolean(), - clean_sess = false :: boolean(), - keep_alive = 60 :: non_neg_integer(), - will_topic = undefined :: undefined | binary(), - will_msg = undefined :: undefined | binary(), - username = undefined :: undefined | binary(), - password = undefined :: undefined | binary()}). +-type(mqtt_client_id() :: binary()). +-type(mqtt_username() :: binary() | undefined). +-type(mqtt_packet_id() :: 1..16#ffff | undefined). --record(mqtt_packet_connack, { - ack_flags = ?RESERVED :: 0 | 1, - return_code :: mqtt_connack() }). +-record(mqtt_packet_connect, + { client_id = <<>> :: mqtt_client_id(), + proto_ver = ?MQTT_PROTO_V4 :: mqtt_vsn(), + proto_name = <<"MQTT">> :: binary(), + will_retain = false :: boolean(), + will_qos = ?QOS_0 :: mqtt_qos(), + will_flag = false :: boolean(), + clean_sess = false :: boolean(), + keep_alive = 60 :: non_neg_integer(), + will_topic = undefined :: undefined | binary(), + will_msg = undefined :: undefined | binary(), + username = undefined :: undefined | binary(), + password = undefined :: undefined | binary() + }). --record(mqtt_packet_publish, { - topic_name :: binary(), - packet_id :: mqtt_packet_id() }). +-record(mqtt_packet_connack, + { ack_flags = ?RESERVED :: 0 | 1, + return_code :: mqtt_connack() + }). --record(mqtt_packet_puback, { - packet_id :: mqtt_packet_id() }). +-record(mqtt_packet_publish, + { topic_name :: binary(), + packet_id :: mqtt_packet_id() + }). --record(mqtt_packet_subscribe, { - packet_id :: mqtt_packet_id(), - topic_table :: list({binary(), mqtt_qos()}) }). +-record(mqtt_packet_puback, + { packet_id :: mqtt_packet_id() }). --record(mqtt_packet_unsubscribe, { - packet_id :: mqtt_packet_id(), - topics :: list(binary()) }). +-record(mqtt_packet_subscribe, + { packet_id :: mqtt_packet_id(), + topic_table :: list({binary(), mqtt_qos()}) + }). --record(mqtt_packet_suback, { - packet_id :: mqtt_packet_id(), - qos_table :: list(mqtt_qos() | 128) }). +-record(mqtt_packet_unsubscribe, + { packet_id :: mqtt_packet_id(), + topics :: list(binary()) + }). --record(mqtt_packet_unsuback, { - packet_id :: mqtt_packet_id() }). +-record(mqtt_packet_suback, + { packet_id :: mqtt_packet_id(), + qos_table :: list(mqtt_qos() | 128) + }). + +-record(mqtt_packet_unsuback, + { packet_id :: mqtt_packet_id() }). %%-------------------------------------------------------------------- %% MQTT Control Packet %%-------------------------------------------------------------------- --record(mqtt_packet, { - header :: #mqtt_packet_header{}, - variable :: #mqtt_packet_connect{} | #mqtt_packet_connack{} - | #mqtt_packet_publish{} | #mqtt_packet_puback{} - | #mqtt_packet_subscribe{} | #mqtt_packet_suback{} - | #mqtt_packet_unsubscribe{} | #mqtt_packet_unsuback{} - | mqtt_packet_id() | undefined, - payload :: binary() | undefined }). --type mqtt_packet() :: #mqtt_packet{}. +-record(mqtt_packet, + { header :: #mqtt_packet_header{}, + variable :: #mqtt_packet_connect{} | #mqtt_packet_connack{} + | #mqtt_packet_publish{} | #mqtt_packet_puback{} + | #mqtt_packet_subscribe{} | #mqtt_packet_suback{} + | #mqtt_packet_unsubscribe{} | #mqtt_packet_unsuback{} + | mqtt_packet_id() | undefined, + payload :: binary() | undefined + }). + +-type(mqtt_packet() :: #mqtt_packet{}). %%-------------------------------------------------------------------- %% MQTT Packet Match %%-------------------------------------------------------------------- + -define(CONNECT_PACKET(Var), #mqtt_packet{header = #mqtt_packet_header{type = ?CONNECT}, variable = Var}). diff --git a/include/emqttd_trie.hrl b/include/emqttd_trie.hrl index bd4184ad8..eb4e1390d 100644 --- a/include/emqttd_trie.hrl +++ b/include/emqttd_trie.hrl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2016-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -16,20 +16,20 @@ -type(trie_node_id() :: binary() | atom()). --record(trie_node, { - node_id :: trie_node_id(), - edge_count = 0 :: non_neg_integer(), - topic :: binary() | undefined, - flags :: [retained | static] -}). +-record(trie_node, + { node_id :: trie_node_id(), + edge_count = 0 :: non_neg_integer(), + topic :: binary() | undefined, + flags :: [retained | static] + }). --record(trie_edge, { - node_id :: trie_node_id(), - word :: binary() | atom() -}). +-record(trie_edge, + { node_id :: trie_node_id(), + word :: binary() | atom() + }). --record(trie, { - edge :: #trie_edge{}, - node_id :: trie_node_id() -}). +-record(trie, + { edge :: #trie_edge{}, + node_id :: trie_node_id() + }). From cae247be97acda2466ca89b404350bcdbe30cf88 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Thu, 16 Feb 2017 10:56:53 +0800 Subject: [PATCH 017/125] Rename 'emqttd_hook' module to 'emqttd_hooks' --- src/emqttd.erl | 16 +++++++++------- src/emqttd_hooks.erl | 12 ++++-------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/emqttd.erl b/src/emqttd.erl index 23db807cc..63b53d4e1 100644 --- a/src/emqttd.erl +++ b/src/emqttd.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -14,10 +14,12 @@ %% limitations under the License. %%-------------------------------------------------------------------- -%% Facade Module for The EMQ Broker +%% @doc EMQ Main Module. -module(emqttd). +-author("Feng Lee "). + -include("emqttd.hrl"). -include("emqttd_protocol.hrl"). @@ -138,23 +140,23 @@ subscriber_down(Subscriber) -> -spec(hook(atom(), function(), list(any())) -> ok | {error, any()}). hook(Hook, Function, InitArgs) -> - emqttd_hook:add(Hook, Function, InitArgs). + emqttd_hooks:add(Hook, Function, InitArgs). -spec(hook(atom(), function(), list(any()), integer()) -> ok | {error, any()}). hook(Hook, Function, InitArgs, Priority) -> - emqttd_hook:add(Hook, Function, InitArgs, Priority). + emqttd_hooks:add(Hook, Function, InitArgs, Priority). -spec(unhook(atom(), function()) -> ok | {error, any()}). unhook(Hook, Function) -> - emqttd_hook:delete(Hook, Function). + emqttd_hooks:delete(Hook, Function). -spec(run_hooks(atom(), list(any())) -> ok | stop). run_hooks(Hook, Args) -> - emqttd_hook:run(Hook, Args). + emqttd_hooks:run(Hook, Args). -spec(run_hooks(atom(), list(any()), any()) -> {ok | stop, any()}). run_hooks(Hook, Args, Acc) -> - emqttd_hook:run(Hook, Args, Acc). + emqttd_hooks:run(Hook, Args, Acc). %%-------------------------------------------------------------------- %% Debug diff --git a/src/emqttd_hooks.erl b/src/emqttd_hooks.erl index 675698688..ce2691894 100644 --- a/src/emqttd_hooks.erl +++ b/src/emqttd_hooks.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2016 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -14,12 +14,12 @@ %% limitations under the License. %%-------------------------------------------------------------------- --module(emqttd_hook). - --author("Feng Lee "). +-module(emqttd_hooks). -behaviour(gen_server). +-author("Feng Lee "). + %% Start -export([start_link/0]). @@ -40,10 +40,6 @@ -define(HOOK_TAB, mqtt_hook). -%%-------------------------------------------------------------------- -%% Start API -%%-------------------------------------------------------------------- - start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). From 9e0b2ed57b7de50f5a967918eaffb959e3059731 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Thu, 16 Feb 2017 11:07:47 +0800 Subject: [PATCH 018/125] Update copyright and change the format of -type, -callback --- src/emqttd_access_control.erl | 22 +++++++++------------- src/emqttd_access_rule.erl | 17 ++++++++++------- src/emqttd_acl_internal.erl | 4 +++- src/emqttd_acl_mod.erl | 11 ++++++----- src/emqttd_alarm.erl | 4 +++- src/emqttd_auth_mod.erl | 12 +++++++----- src/emqttd_base62.erl | 4 +++- src/emqttd_boot.erl | 4 +++- src/emqttd_bridge.erl | 8 +++++--- src/emqttd_bridge_sup.erl | 2 +- src/emqttd_bridge_sup_sup.erl | 6 ++++-- src/emqttd_broker.erl | 24 +++++++++++++----------- src/emqttd_cluster.erl | 4 +++- src/emqttd_ctl.erl | 7 ++++++- src/emqttd_gen_mod.erl | 10 ++++++---- src/emqttd_guid.erl | 5 +++-- src/emqttd_http.erl | 7 +++++-- src/emqttd_keepalive.erl | 13 ++++++++----- src/emqttd_mnesia.erl | 4 +++- src/emqttd_mod_sup.erl | 8 ++------ src/emqttd_net.erl | 3 +-- src/emqttd_node.erl | 4 +++- src/emqttd_pool_sup.erl | 4 +++- src/emqttd_pooler.erl | 3 ++- src/emqttd_pubsub.erl | 9 +++++---- src/emqttd_pubsub_sup.erl | 4 +++- src/emqttd_sysmon.erl | 4 +++- src/emqttd_sysmon_sup.erl | 2 +- src/emqttd_trace.erl | 10 +++++----- src/emqttd_trace_sup.erl | 8 +++++--- src/emqttd_trie.erl | 5 ++++- src/emqttd_vm.erl | 2 +- src/lager_emqtt_backend.erl | 4 +++- 33 files changed, 142 insertions(+), 96 deletions(-) diff --git a/src/emqttd_access_control.erl b/src/emqttd_access_control.erl index 7aea58e79..cc60a57f2 100644 --- a/src/emqttd_access_control.erl +++ b/src/emqttd_access_control.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -16,29 +16,25 @@ -module(emqttd_access_control). --include("emqttd.hrl"). - -behaviour(gen_server). --define(SERVER, ?MODULE). +-author("Feng Lee "). + +-include("emqttd.hrl"). %% API Function Exports --export([start_link/0, - auth/2, % authentication - check_acl/3, % acl check - reload_acl/0, % reload acl - lookup_mods/1, - register_mod/3, register_mod/4, - unregister_mod/2, - stop/0]). +-export([start_link/0, auth/2, check_acl/3, reload_acl/0, lookup_mods/1, + register_mod/3, register_mod/4, unregister_mod/2, stop/0]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). +-define(SERVER, ?MODULE). + -define(ACCESS_CONTROL_TAB, mqtt_access_control). --type password() :: undefined | binary(). +-type(password() :: undefined | binary()). -record(state, {}). diff --git a/src/emqttd_access_rule.erl b/src/emqttd_access_rule.erl index 1590d2efe..73718fd3a 100644 --- a/src/emqttd_access_rule.erl +++ b/src/emqttd_access_rule.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -16,21 +16,24 @@ -module(emqttd_access_rule). +-author("Feng Lee "). + -include("emqttd.hrl"). --type who() :: all | binary() | + +-type(who() :: all | binary() | {ipaddr, esockd_cidr:cidr_string()} | {client, binary()} | - {user, binary()}. + {user, binary()}). --type access() :: subscribe | publish | pubsub. +-type(access() :: subscribe | publish | pubsub). --type topic() :: binary(). +-type(topic() :: binary()). --type rule() :: {allow, all} | +-type(rule() :: {allow, all} | {allow, who(), access(), list(topic())} | {deny, all} | - {deny, who(), access(), list(topic())}. + {deny, who(), access(), list(topic())}). -export_type([rule/0]). diff --git a/src/emqttd_acl_internal.erl b/src/emqttd_acl_internal.erl index 74ab8519a..f1c8b801b 100644 --- a/src/emqttd_acl_internal.erl +++ b/src/emqttd_acl_internal.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -18,6 +18,8 @@ -behaviour(emqttd_acl_mod). +-author("Feng Lee "). + -include("emqttd.hrl"). -export([all_rules/0]). diff --git a/src/emqttd_acl_mod.erl b/src/emqttd_acl_mod.erl index 44c775460..12e949afe 100644 --- a/src/emqttd_acl_mod.erl +++ b/src/emqttd_acl_mod.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -16,6 +16,8 @@ -module(emqttd_acl_mod). +-author("Feng Lee "). + -include("emqttd.hrl"). %%-------------------------------------------------------------------- @@ -26,10 +28,9 @@ -callback(init(AclOpts :: list()) -> {ok, State :: any()}). --callback(check_acl({Client, PubSub, Topic}, State :: any()) -> allow | deny | ignore when - Client :: mqtt_client(), - PubSub :: pubsub(), - Topic :: binary()). +-callback(check_acl({Client :: mqtt_client(), + PubSub :: pubsub(), + Topic :: binary()}, State :: any()) -> allow | deny | ignore). -callback(reload_acl(State :: any()) -> ok | {error, any()}). diff --git a/src/emqttd_alarm.erl b/src/emqttd_alarm.erl index 1ff4a5ae9..b214ab8fc 100644 --- a/src/emqttd_alarm.erl +++ b/src/emqttd_alarm.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -16,6 +16,8 @@ -module(emqttd_alarm). +-author("Feng Lee "). + -behaviour(gen_event). -include("emqttd.hrl"). diff --git a/src/emqttd_auth_mod.erl b/src/emqttd_auth_mod.erl index d6d4a55b0..c27f49d36 100644 --- a/src/emqttd_auth_mod.erl +++ b/src/emqttd_auth_mod.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -16,6 +16,8 @@ -module(emqttd_auth_mod). +-author("Feng Lee "). + -include("emqttd.hrl"). -export([passwd_hash/2]). @@ -30,10 +32,10 @@ -callback(init(AuthOpts :: list()) -> {ok, State :: any()}). --callback(check(Client, Password, State) -> ok | | {ok, boolean()} | ignore | {error, string()} when - Client :: mqtt_client(), - Password :: binary(), - State :: any()). +-callback(check(Client :: mqtt_client(), + Password :: binary(), + State :: any()) + -> ok | | {ok, boolean()} | ignore | {error, string()}). -callback(description() -> string()). diff --git a/src/emqttd_base62.erl b/src/emqttd_base62.erl index 5895fa245..481488fb9 100644 --- a/src/emqttd_base62.erl +++ b/src/emqttd_base62.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2016-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -16,6 +16,8 @@ -module(emqttd_base62). +-author("Feng Lee "). + -export([encode/1, decode/1]). %% @doc Encode an integer to base62 string diff --git a/src/emqttd_boot.erl b/src/emqttd_boot.erl index dd1b8a65b..d7a6d311e 100644 --- a/src/emqttd_boot.erl +++ b/src/emqttd_boot.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -16,6 +16,8 @@ -module(emqttd_boot). +-author("Feng Lee "). + -export([apply_module_attributes/1, all_module_attributes/1]). %% only {F, Args}... diff --git a/src/emqttd_bridge.erl b/src/emqttd_bridge.erl index 3dca7bf0f..7e36ad256 100644 --- a/src/emqttd_bridge.erl +++ b/src/emqttd_bridge.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -18,6 +18,8 @@ -behaviour(gen_server2). +-author("Feng Lee "). + -include("emqttd.hrl"). -include("emqttd_protocol.hrl"). @@ -43,11 +45,11 @@ ping_down_interval = ?PING_DOWN_INTERVAL, status = up}). --type option() :: {qos, mqtt_qos()} | +-type(option() :: {qos, mqtt_qos()} | {topic_suffix, binary()} | {topic_prefix, binary()} | {max_queue_len, pos_integer()} | - {ping_down_interval, pos_integer()}. + {ping_down_interval, pos_integer()}). -export_type([option/0]). diff --git a/src/emqttd_bridge_sup.erl b/src/emqttd_bridge_sup.erl index dd418b2f6..d28b2274c 100644 --- a/src/emqttd_bridge_sup.erl +++ b/src/emqttd_bridge_sup.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_bridge_sup_sup.erl b/src/emqttd_bridge_sup_sup.erl index 47e246273..1d47bd003 100644 --- a/src/emqttd_bridge_sup_sup.erl +++ b/src/emqttd_bridge_sup_sup.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -18,6 +18,8 @@ -behavior(supervisor). +-author("Feng Lee "). + -export([start_link/0, bridges/0, start_bridge/2, start_bridge/3, stop_bridge/2]). -export([init/1]). @@ -47,7 +49,7 @@ start_bridge(Node, _Topic, _Options) when Node =:= node() -> {error, bridge_to_self}; start_bridge(Node, Topic, Options) when is_atom(Node) andalso is_binary(Topic) -> {ok, BridgeEnv} = emqttd:env(bridge), - Options1 = emqttd_opts:merge(BridgeEnv, Options), + Options1 = emqttd_misc:merge_opts(BridgeEnv, Options), supervisor:start_child(?MODULE, bridge_spec(Node, Topic, Options1)). %% @doc Stop a bridge diff --git a/src/emqttd_broker.erl b/src/emqttd_broker.erl index 96dd7d553..ce2e4210c 100644 --- a/src/emqttd_broker.erl +++ b/src/emqttd_broker.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -18,6 +18,8 @@ -behaviour(gen_server). +-author("Feng Lee "). + -include("emqttd.hrl"). -include("emqttd_internal.hrl"). @@ -40,16 +42,18 @@ -record(state, {started_at, sys_interval, heartbeat, tick_tref, version, sysdescr}). +-define(APP, emqttd). + -define(SERVER, ?MODULE). -define(BROKER_TAB, mqtt_broker). %% $SYS Topics of Broker -define(SYSTOP_BROKERS, [ - version, % Broker version - uptime, % Broker uptime - datetime, % Broker local datetime - sysdescr % Broker description + version, % Broker version + uptime, % Broker uptime + datetime, % Broker local datetime + sysdescr % Broker description ]). %%-------------------------------------------------------------------- @@ -74,12 +78,12 @@ notify(EventType, Event) -> %% @doc Get broker version -spec(version() -> string()). version() -> - {ok, Version} = application:get_key(emqttd, vsn), Version. + {ok, Version} = application:get_key(?APP, vsn), Version. %% @doc Get broker description -spec(sysdescr() -> string()). sysdescr() -> - {ok, Descr} = application:get_key(emqttd, description), Descr. + {ok, Descr} = application:get_key(?APP, description), Descr. %% @doc Get broker uptime -spec(uptime() -> string()). @@ -161,13 +165,11 @@ retain(brokers) -> Payload = list_to_binary(string:join([atom_to_list(N) || N <- emqttd_mnesia:running_nodes()], ",")), Msg = emqttd_message:make(broker, <<"$SYS/brokers">>, Payload), - Msg1 = emqttd_message:set_flag(sys, emqttd_message:set_flag(retain, Msg)), - emqttd:publish(Msg1). + emqttd:publish(emqttd_message:set_flag(sys, emqttd_message:set_flag(retain, Msg))). retain(Topic, Payload) when is_binary(Payload) -> Msg = emqttd_message:make(broker, emqttd_topic:systop(Topic), Payload), - Msg1 = emqttd_message:set_flag(sys, emqttd_message:set_flag(retain, Msg)), - emqttd:publish(Msg1). + emqttd:publish(emqttd_message:set_flag(sys, emqttd_message:set_flag(retain, Msg))). publish(Topic, Payload) when is_binary(Payload) -> Msg = emqttd_message:make(broker, emqttd_topic:systop(Topic), Payload), diff --git a/src/emqttd_cluster.erl b/src/emqttd_cluster.erl index 7c83c079c..7990a5d51 100644 --- a/src/emqttd_cluster.erl +++ b/src/emqttd_cluster.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -16,6 +16,8 @@ -module(emqttd_cluster). +-author("Feng Lee "). + -include("emqttd.hrl"). %% Cluster API diff --git a/src/emqttd_ctl.erl b/src/emqttd_ctl.erl index c0b7dcf70..0aa8ce353 100644 --- a/src/emqttd_ctl.erl +++ b/src/emqttd_ctl.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -18,6 +18,8 @@ -behaviour(gen_server). +-author("Feng Lee "). + -include("emqttd.hrl"). -include("emqttd_cli.hrl"). @@ -134,7 +136,9 @@ next_seq(State = #state{seq = Seq}) -> State#state{seq = Seq + 1}. -ifdef(TEST). + -include_lib("eunit/include/eunit.hrl"). + register_cmd_test_() -> {setup, fun() -> @@ -149,4 +153,5 @@ register_cmd_test_() -> [?_assertMatch([{{0,test0},{?MODULE, test0}, []}], ets:lookup(?CMD_TAB, {Seq,test0}))] end }. + -endif. diff --git a/src/emqttd_gen_mod.erl b/src/emqttd_gen_mod.erl index b201f7bdd..824b65f4c 100644 --- a/src/emqttd_gen_mod.erl +++ b/src/emqttd_gen_mod.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -15,15 +15,18 @@ %%-------------------------------------------------------------------- %% @doc emqttd gen_mod behaviour + -module(emqttd_gen_mod). +-author("Feng Lee "). + -include("emqttd.hrl"). -ifdef(use_specs). --callback load(Opts :: any()) -> ok | {error, any()}. +-callback(load(Opts :: any()) -> ok | {error, any()}). --callback unload(State :: any()) -> any(). +-callback(unload(State :: any()) -> any()). -else. @@ -35,4 +38,3 @@ behaviour_info(_Other) -> undefined. -endif. - diff --git a/src/emqttd_guid.erl b/src/emqttd_guid.erl index a4c2d1ea4..24199fa01 100644 --- a/src/emqttd_guid.erl +++ b/src/emqttd_guid.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -27,13 +27,14 @@ %% 4. Sequence: 2 bytes sequence in one process %% %% @end + -module(emqttd_guid). -export([gen/0, new/0, timestamp/1, to_hexstr/1, from_hexstr/1, to_base62/1, from_base62/1]). -define(MAX_SEQ, 16#FFFF). --type guid() :: <<_:128>>. +-type(guid() :: <<_:128>>). %% @doc Generate a global unique id. -spec(gen() -> guid()). diff --git a/src/emqttd_http.erl b/src/emqttd_http.erl index 8ad37dd10..692477447 100644 --- a/src/emqttd_http.erl +++ b/src/emqttd_http.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -14,9 +14,12 @@ %% limitations under the License. %%-------------------------------------------------------------------- -%% @doc emqttd http publish API and websocket client. +%% @doc HTTP publish API and websocket client. + -module(emqttd_http). +-author("Feng Lee "). + -include("emqttd.hrl"). -include("emqttd_protocol.hrl"). diff --git a/src/emqttd_keepalive.erl b/src/emqttd_keepalive.erl index 54a4d20e5..51d45c89c 100644 --- a/src/emqttd_keepalive.erl +++ b/src/emqttd_keepalive.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -15,15 +15,18 @@ %%-------------------------------------------------------------------- %% @doc Client Keepalive + -module(emqttd_keepalive). +-author("Feng Lee "). + -export([start/3, check/1, cancel/1]). --record(keepalive, {statfun, statval, - tsec, tmsg, tref, - repeat = 0}). +-record(keepalive, {statfun, statval, tsec, tmsg, tref, repeat = 0}). --type keepalive() :: #keepalive{}. +-type(keepalive() :: #keepalive{}). + +-export_type([keepalive/0]). %% @doc Start a keepalive -spec(start(fun(), integer(), any()) -> undefined | keepalive()). diff --git a/src/emqttd_mnesia.erl b/src/emqttd_mnesia.erl index 52dca1d1b..31fa88d3a 100644 --- a/src/emqttd_mnesia.erl +++ b/src/emqttd_mnesia.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -16,6 +16,8 @@ -module(emqttd_mnesia). +-author("Feng Lee "). + -include("emqttd.hrl"). -include("emqttd_internal.hrl"). diff --git a/src/emqttd_mod_sup.erl b/src/emqttd_mod_sup.erl index 57b13bc37..586d5af1a 100644 --- a/src/emqttd_mod_sup.erl +++ b/src/emqttd_mod_sup.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -39,11 +39,7 @@ start_link() -> start_child(ChildSpec) when is_tuple(ChildSpec) -> supervisor:start_child(?MODULE, ChildSpec). -%% -%% start_child(Mod::atom(), Type::type()) -> {ok, pid()} -%% @type type() = worker | supervisor -%% -start_child(Mod, Type) when is_atom(Mod) and is_atom(Type) -> +start_child(Mod, Type) when is_atom(Mod) andalso is_atom(Type) -> supervisor:start_child(?MODULE, ?CHILD(Mod, Type)). -spec(stop_child(any()) -> ok | {error, any()}). diff --git a/src/emqttd_net.erl b/src/emqttd_net.erl index 8278e1f74..1f246a315 100644 --- a/src/emqttd_net.erl +++ b/src/emqttd_net.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -14,7 +14,6 @@ %% limitations under the License. %%-------------------------------------------------------------------- -%% @doc emqttd net utilities. -module(emqttd_net). -include_lib("kernel/include/inet.hrl"). diff --git a/src/emqttd_node.erl b/src/emqttd_node.erl index e48f71fae..ecf44503f 100644 --- a/src/emqttd_node.erl +++ b/src/emqttd_node.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -16,6 +16,8 @@ -module(emqttd_node). +-author("Feng Lee "). + -import(lists, [concat/1]). -export([is_aliving/1, parse_name/1]). diff --git a/src/emqttd_pool_sup.erl b/src/emqttd_pool_sup.erl index e06d09e8f..bc8f59562 100644 --- a/src/emqttd_pool_sup.erl +++ b/src/emqttd_pool_sup.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -17,6 +17,8 @@ %% @doc Common Pool Supervisor -module(emqttd_pool_sup). +-author("Feng Lee "). + -behaviour(supervisor). %% API diff --git a/src/emqttd_pooler.erl b/src/emqttd_pooler.erl index 2701c470e..1b73d138c 100644 --- a/src/emqttd_pooler.erl +++ b/src/emqttd_pooler.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -39,6 +39,7 @@ start_link() -> %%-------------------------------------------------------------------- %% API %%-------------------------------------------------------------------- + -spec(start_link(atom(), pos_integer()) -> {ok, pid()} | ignore | {error, any()}). start_link(Pool, Id) -> gen_server:start_link({local, ?PROC_NAME(?MODULE, Id)}, ?MODULE, [Pool, Id], []). diff --git a/src/emqttd_pubsub.erl b/src/emqttd_pubsub.erl index 83a080ff6..c99aca513 100644 --- a/src/emqttd_pubsub.erl +++ b/src/emqttd_pubsub.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -18,11 +18,12 @@ -behaviour(gen_server2). +-author("Feng Lee "). + -include("emqttd.hrl"). -include("emqttd_internal.hrl"). -%% Start API. -export([start_link/3]). %% PubSub API. @@ -31,7 +32,7 @@ -export([dispatch/2]). -%% gen_server. +%% gen_server Callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). @@ -45,7 +46,6 @@ %% Start PubSub %%-------------------------------------------------------------------- -%% @doc Start one Pubsub -spec(start_link(atom(), pos_integer(), list()) -> {ok, pid()} | ignore | {error, any()}). start_link(Pool, Id, Env) -> gen_server2:start_link({local, ?PROC_NAME(?MODULE, Id)}, ?MODULE, [Pool, Id, Env], []). @@ -115,6 +115,7 @@ dispatch({_Share, [Sub]}, Topic, Msg) -> dispatch(Sub, Topic, Msg); dispatch({_Share, []}, _Topic, _Msg) -> ok; +%%TODO: round-robbin dispatch({_Share, Subs}, Topic, Msg) -> dispatch(lists:nth(rand:uniform(length(Subs)), Subs), Topic, Msg). diff --git a/src/emqttd_pubsub_sup.erl b/src/emqttd_pubsub_sup.erl index 882fce57c..09d08d110 100644 --- a/src/emqttd_pubsub_sup.erl +++ b/src/emqttd_pubsub_sup.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -17,6 +17,8 @@ %% @doc PubSub Supervisor. -module(emqttd_pubsub_sup). +-author("Feng Lee "). + -behaviour(supervisor). %% API diff --git a/src/emqttd_sysmon.erl b/src/emqttd_sysmon.erl index 7c338a842..c94c1df54 100644 --- a/src/emqttd_sysmon.erl +++ b/src/emqttd_sysmon.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -17,6 +17,8 @@ %% @doc VM System Monitor -module(emqttd_sysmon). +-author("Feng Lee "). + -behavior(gen_server). -include("emqttd_internal.hrl"). diff --git a/src/emqttd_sysmon_sup.erl b/src/emqttd_sysmon_sup.erl index 0131c42db..99e7a628d 100644 --- a/src/emqttd_sysmon_sup.erl +++ b/src/emqttd_sysmon_sup.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/emqttd_trace.erl b/src/emqttd_trace.erl index 069f44937..4d46d9165 100644 --- a/src/emqttd_trace.erl +++ b/src/emqttd_trace.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -14,13 +14,13 @@ %% limitations under the License. %%-------------------------------------------------------------------- -%% @doc -%% Trace MQTT packets/messages by ClientID or Topic. -%% @end +%% @docTrace MQTT packets/messages by ClientID or Topic. -module(emqttd_trace). -behaviour(gen_server). +-author("Feng Lee "). + -include("emqttd_internal.hrl"). %% API Function Exports @@ -34,7 +34,7 @@ -record(state, {level, traces}). --type trace_who() :: {client | topic, binary()}. +-type(trace_who() :: {client | topic, binary()}). -define(TRACE_OPTIONS, [{formatter_config, [time, " [",severity,"] ", message, "\n"]}]). diff --git a/src/emqttd_trace_sup.erl b/src/emqttd_trace_sup.erl index 22736b1a2..728e6818e 100644 --- a/src/emqttd_trace_sup.erl +++ b/src/emqttd_trace_sup.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -16,6 +16,8 @@ -module(emqttd_trace_sup). +-author("Feng Lee "). + -behaviour(supervisor). %% API @@ -29,6 +31,6 @@ start_link() -> init([]) -> Trace = {trace, {emqttd_trace, start_link, []}, - permanent, 5000, worker, [emqttd_trace]}, - {ok, {{one_for_one, 10, 100}, [Trace]}}. + permanent, 5000, worker, [emqttd_trace]}, + {ok, {{one_for_one, 10, 3600}, [Trace]}}. diff --git a/src/emqttd_trie.erl b/src/emqttd_trie.erl index 23bb40bcf..70b1c003f 100644 --- a/src/emqttd_trie.erl +++ b/src/emqttd_trie.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -17,8 +17,11 @@ %% @doc MQTT Topic Trie: %% [Trie](http://en.wikipedia.org/wiki/Trie) %% @end + -module(emqttd_trie). +-author("Feng Lee "). + -include("emqttd_trie.hrl"). %% Mnesia Callbacks diff --git a/src/emqttd_vm.erl b/src/emqttd_vm.erl index abf4d3b78..16fae60ae 100644 --- a/src/emqttd_vm.erl +++ b/src/emqttd_vm.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/lager_emqtt_backend.erl b/src/lager_emqtt_backend.erl index 214182b8d..69c1aece4 100644 --- a/src/lager_emqtt_backend.erl +++ b/src/lager_emqtt_backend.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -16,6 +16,8 @@ -module(lager_emqtt_backend). +-author("Feng Lee "). + -behaviour(gen_event). -include_lib("lager/include/lager.hrl"). From 0019fb2b49788720c00a0e38d9e326325542d2e3 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Thu, 16 Feb 2017 11:10:47 +0800 Subject: [PATCH 019/125] Change the message timestamp to 'os:timestamp()' --- src/emqttd_message.erl | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/emqttd_message.erl b/src/emqttd_message.erl index ec4a02479..4c3bea0d8 100644 --- a/src/emqttd_message.erl +++ b/src/emqttd_message.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -15,8 +15,11 @@ %%-------------------------------------------------------------------- %% @doc MQTT Message Functions + -module(emqttd_message). +-author("Feng Lee "). + -include("emqttd.hrl"). -include("emqttd_protocol.hrl"). @@ -42,7 +45,7 @@ make(From, Qos, Topic, Payload) -> qos = ?QOS_I(Qos), topic = Topic, payload = Payload, - timestamp = emqttd_time:now_to_secs()}. + timestamp = os:timestamp()}. %% @doc Message from Packet -spec(from_packet(mqtt_packet()) -> mqtt_message()). @@ -60,7 +63,7 @@ from_packet(#mqtt_packet{header = #mqtt_packet_header{type = ?PUBLISH, dup = Dup, topic = Topic, payload = Payload, - timestamp = emqttd_time:now_to_secs()}; + timestamp = os:timestamp()}; from_packet(#mqtt_packet_connect{will_flag = false}) -> undefined; @@ -78,7 +81,7 @@ from_packet(#mqtt_packet_connect{client_id = ClientId, qos = Qos, dup = false, payload = Msg, - timestamp = emqttd_time:now_to_secs()}. + timestamp = os:timestamp()}. from_packet(ClientId, Packet) -> Msg = from_packet(Packet), From 796d5df1c0ea1fded71549f6874be075bd55c9b6 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Thu, 16 Feb 2017 11:17:57 +0800 Subject: [PATCH 020/125] Upgrade parse/2 function --- src/emqttd_topic.erl | 57 ++++++++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/src/emqttd_topic.erl b/src/emqttd_topic.erl index 0afeacae8..458a41f7d 100644 --- a/src/emqttd_topic.erl +++ b/src/emqttd_topic.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -16,8 +16,12 @@ -module(emqttd_topic). +-author("Feng Lee "). + -include("emqttd_protocol.hrl"). +-include("emqttd_internal.hrl"). + -import(lists, [reverse/1]). -export([match/2, validate/1, triples/1, words/1, wildcard/1]). @@ -55,8 +59,8 @@ wildcard([_H|T]) -> %% @doc Match Topic name with filter -spec(match(Name, Filter) -> boolean() when - Name :: topic() | words(), - Filter :: topic() | words()). + Name :: topic() | words(), + Filter :: topic() | words()). match(Name, Filter) when is_binary(Name) and is_binary(Filter) -> match(words(Name), words(Filter)); match([], []) -> @@ -94,17 +98,14 @@ validate2([]) -> true; validate2(['#']) -> % end with '#' true; -validate2(['#'|Words]) when length(Words) > 0 -> - false; +validate2(['#'|Words]) when length(Words) > 0 -> + false; validate2([''|Words]) -> validate2(Words); validate2(['+'|Words]) -> validate2(Words); validate2([W|Words]) -> - case validate3(W) of - true -> validate2(Words); - false -> false - end. + case validate3(W) of true -> validate2(Words); false -> false end. validate3(<<>>) -> true; @@ -180,24 +181,28 @@ join(Words) -> parse(Topic) when is_binary(Topic) -> parse(Topic, []). -parse(Topic = <<"$local/", Topic1/binary>>, Options) -> - case lists:member(local, Options) of - true -> error({invalid_topic, Topic}); - false -> parse(Topic1, [local | Options]) - end; +parse(<<"$local/", Topic1/binary>>, Options) -> + if_not_contain(local, Options, fun() -> + parse(Topic1, [local | Options]) + end); -parse(Topic = <<"$queue/", Topic1/binary>>, Options) -> - case lists:keyfind(share, 1, Options) of - {share, _} -> error({invalid_topic, Topic}); - false -> parse(Topic1, [{share, '$queue'} | Options]) - end; +parse(<<"$queue/", Topic1/binary>>, Options) -> + if_not_contain(share, Options,fun() -> + parse(Topic1, [{share, '$queue'} | Options]) + end); -parse(Topic = <<"$share/", Topic1/binary>>, Options) -> - case lists:keyfind(share, 1, Options) of - {share, _} -> error({invalid_topic, Topic}); - false -> [Share, Topic2] = binary:split(Topic1, <<"/">>), - {Topic2, [{share, Share} | Options]} - end; +parse(<<"$share/", Topic1/binary>>, Options) -> + if_not_contain(share, Options, fun() -> + [Share, Topic2] = binary:split(Topic1, <<"/">>), + {Topic2, [{share, Share} | Options]} + end); -parse(Topic, Options) -> {Topic, Options}. +parse(Topic, Options) -> + {Topic, Options}. + +if_not_contain(local, Options, Fun) -> + ?IF(lists:member(local, Options), error(invalid_topic), Fun()); + +if_not_contain(share, Options, Fun) -> + ?IF(lists:keyfind(share, 1, Options), error(invalid_topic), Fun()). From 640c9287c2093dab7fce98adf244e17d10aaa569 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Thu, 16 Feb 2017 11:18:37 +0800 Subject: [PATCH 021/125] Rename now_to_secs, now_to_ms functions --- src/emqttd_time.erl | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/emqttd_time.erl b/src/emqttd_time.erl index 49cf4965c..f9ada795d 100644 --- a/src/emqttd_time.erl +++ b/src/emqttd_time.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -16,21 +16,27 @@ -module(emqttd_time). --export([seed/0, now_to_secs/0, now_to_secs/1, now_to_ms/0, now_to_ms/1]). +-author("Feng Lee "). + +-export([seed/0, now_secs/0, now_secs/1, now_ms/0, now_ms/1, ts_from_ms/1]). seed() -> case erlang:function_exported(erlang, timestamp, 0) of - true -> random:seed(erlang:timestamp()); %% R18 + true -> rand:seed(exsplus, erlang:timestamp()); %% R18 false -> random:seed(os:timestamp()) %% Compress now() deprecated warning... end. -now_to_secs() -> now_to_secs(os:timestamp()). +now_ms() -> + now_ms(os:timestamp()). -now_to_secs({MegaSecs, Secs, _MicroSecs}) -> - MegaSecs * 1000000 + Secs. - -now_to_ms() -> now_to_ms(os:timestamp()). - -now_to_ms({MegaSecs, Secs, MicroSecs}) -> +now_ms({MegaSecs, Secs, MicroSecs}) -> (MegaSecs * 1000000 + Secs) * 1000 + round(MicroSecs/1000). +now_secs() -> + now_secs(os:timestamp()). + +now_secs({MegaSecs, Secs, _MicroSecs}) -> + MegaSecs * 1000000 + Secs. + +ts_from_ms(Ms) -> + {Ms div 1000000, Ms rem 1000000, 0}. From d93caa7c9ecadd51f31bf5946ae4d6a00c37a8ea Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Thu, 16 Feb 2017 11:19:02 +0800 Subject: [PATCH 022/125] Update copyright and format code --- src/emqttd_cli.erl | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/src/emqttd_cli.erl b/src/emqttd_cli.erl index 085032b24..06faf6879 100644 --- a/src/emqttd_cli.erl +++ b/src/emqttd_cli.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -16,6 +16,8 @@ -module(emqttd_cli). +-author("Feng Lee "). + -include("emqttd.hrl"). -include("emqttd_cli.hrl"). @@ -57,6 +59,7 @@ is_cmd(Fun) -> %%-------------------------------------------------------------------- %% @doc Node status + status([]) -> {InternalStatus, _ProvidedStatus} = init:get_status(), ?PRINT("Node ~p is ~p~n", [node(), InternalStatus]), @@ -71,6 +74,7 @@ status(_) -> %%-------------------------------------------------------------------- %% @doc Query broker + broker([]) -> Funs = [sysdescr, version, uptime, datetime], foreach(fun(Fun) -> @@ -105,6 +109,7 @@ broker(_) -> %%-------------------------------------------------------------------- %% @doc Cluster with other nodes + cluster(["join", SNode]) -> case emqttd_cluster:join(emqttd_node:parse_name(SNode)) of ok -> @@ -143,10 +148,12 @@ cluster(_) -> %%-------------------------------------------------------------------- %% @doc Users usage + users(Args) -> emq_auth_username:cli(Args). %%-------------------------------------------------------------------- %% @doc Query clients + clients(["list"]) -> dump(mqtt_client); @@ -169,14 +176,17 @@ if_client(ClientId, Fun) -> %%-------------------------------------------------------------------- %% @doc Sessions Command + sessions(["list"]) -> dump(mqtt_local_session); %% performance issue? + sessions(["list", "persistent"]) -> lists:foreach(fun print/1, ets:match_object(mqtt_local_session, {'_', '_', false, '_'})); %% performance issue? + sessions(["list", "transient"]) -> lists:foreach(fun print/1, ets:match_object(mqtt_local_session, {'_', '_', true, '_'})); @@ -194,6 +204,7 @@ sessions(_) -> %%-------------------------------------------------------------------- %% @doc Routes Command + routes(["list"]) -> Routes = emqttd_router:dump(), foreach(fun print/1, Routes); @@ -207,6 +218,7 @@ routes(_) -> %%-------------------------------------------------------------------- %% @doc Topics Command + topics(["list"]) -> lists:foreach(fun(Topic) -> ?PRINT("~s~n", [Topic]) end, emqttd:topics()); @@ -260,14 +272,14 @@ subscriptions(_) -> {"subscriptions del ", "Delete static subscriptions manually"}, {"subscriptions del ", "Delete a static subscription manually"}]). -if_could_print(Tab, Fun) -> - case mnesia:table_info(Tab, size) of - Size when Size >= ?MAX_LIMIT -> - ?PRINT("Could not list, too many ~ss: ~p~n", [Tab, Size]); - _Size -> - Keys = mnesia:dirty_all_keys(Tab), - foreach(fun(Key) -> Fun(ets:lookup(Tab, Key)) end, Keys) - end. +% if_could_print(Tab, Fun) -> +% case mnesia:table_info(Tab, size) of +% Size when Size >= ?MAX_LIMIT -> +% ?PRINT("Could not list, too many ~ss: ~p~n", [Tab, Size]); +% _Size -> +% Keys = mnesia:dirty_all_keys(Tab), +% foreach(fun(Key) -> Fun(ets:lookup(Tab, Key)) end, Keys) +% end. if_valid_qos(QoS, Fun) -> try list_to_integer(QoS) of @@ -303,6 +315,7 @@ plugins(_) -> %%-------------------------------------------------------------------- %% @doc Bridges command + bridges(["list"]) -> foreach(fun({Node, Topic, _Pid}) -> ?PRINT("bridge: ~s--~s-->~s~n", [node(), Topic, Node]) @@ -360,6 +373,7 @@ parse_opt(_Cmd, Opt, _Val) -> %%-------------------------------------------------------------------- %% @doc vm command + vm([]) -> vm(["all"]); @@ -398,6 +412,7 @@ vm(_) -> %%-------------------------------------------------------------------- %% @doc mnesia Command + mnesia([]) -> mnesia:system_info(); @@ -406,6 +421,7 @@ mnesia(_) -> %%-------------------------------------------------------------------- %% @doc Trace Command + trace(["list"]) -> foreach(fun({{Who, Name}, LogFile}) -> ?PRINT("trace ~s ~s -> ~s~n", [Who, Name, LogFile]) @@ -448,6 +464,7 @@ trace_off(Who, Name) -> %%-------------------------------------------------------------------- %% @doc Listeners Command + listeners([]) -> foreach(fun({{Protocol, ListenOn}, Pid}) -> Info = [{acceptors, esockd:get_acceptors(Pid)}, From dbcd79fb749d549e6ee3c510fe144e738806dcfe Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Thu, 16 Feb 2017 11:22:48 +0800 Subject: [PATCH 023/125] Move the 'MQTT_SOCKOPTS' macro to include/emqttd_protocol.hrl --- src/emqttd_app.erl | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/src/emqttd_app.erl b/src/emqttd_app.erl index bdbfc4a54..6f758b870 100644 --- a/src/emqttd_app.erl +++ b/src/emqttd_app.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -18,29 +18,26 @@ -behaviour(application). +-author("Feng Lee "). + -include("emqttd_cli.hrl"). +-include("emqttd_protocol.hrl"). + %% Application callbacks -export([start/2, stop/1]). -export([start_listener/1, stop_listener/1]). -%% MQTT SockOpts --define(MQTT_SOCKOPTS, [binary, {packet, raw}, {reuseaddr, true}, - {backlog, 512}, {nodelay, true}]). +-type(listener() :: {atom(), esockd:listen_on(), [esockd:option()]}). --type listener() :: {atom(), esockd:listen_on(), [esockd:option()]}. +-define(APP, emqttd). %%-------------------------------------------------------------------- %% Application callbacks %%-------------------------------------------------------------------- --spec(start(StartType, StartArgs) -> {ok, pid()} | {ok, pid(), State} | {error, Reason} when - StartType :: normal | {takeover, node()} | {failover, node()}, - StartArgs :: term(), - State :: term(), - Reason :: term()). -start(_StartType, _StartArgs) -> +start(_Type, _Args) -> print_banner(), emqttd_mnesia:start(), {ok, Sup} = emqttd_sup:start_link(), @@ -63,12 +60,11 @@ stop(_State) -> %%-------------------------------------------------------------------- print_banner() -> - ?PRINT("starting emqttd on node '~s'~n", [node()]). + ?PRINT("starting ~s on node '~s'~n", [?APP, node()]). print_vsn() -> {ok, Vsn} = application:get_key(vsn), - {ok, Desc} = application:get_key(description), - ?PRINT("~s ~s is running now~n", [Desc, Vsn]). + ?PRINT("~s ~s is running now~n", [?APP, Vsn]). %%-------------------------------------------------------------------- %% Start Servers @@ -176,14 +172,14 @@ start_listener({Proto, ListenOn, Opts}) when Proto == https; Proto == wss -> mochiweb:start_http('mqtt:wss', ListenOn, Opts, {emqttd_http, handle_request, []}). start_listener(Proto, ListenOn, Opts) -> - {ok, Env} = emqttd:env(protocol), + Env = lists:append(emqttd:env(client, []), emqttd:env(protocol, [])), MFArgs = {emqttd_client, start_link, [Env]}, {ok, _} = esockd:open(Proto, ListenOn, merge_sockopts(Opts), MFArgs). merge_sockopts(Options) -> - SockOpts = emqttd_opts:merge(?MQTT_SOCKOPTS, - proplists:get_value(sockopts, Options, [])), - emqttd_opts:merge(Options, [{sockopts, SockOpts}]). + SockOpts = emqttd_misc:merge_opts( + ?MQTT_SOCKOPTS, proplists:get_value(sockopts, Options, [])), + emqttd_misc:merge_opts(Options, [{sockopts, SockOpts}]). %%-------------------------------------------------------------------- %% Stop Listeners @@ -211,3 +207,4 @@ merge_sockopts_test_() -> ?_assert(merge_sockopts(Opts) == [{sockopts, ?MQTT_SOCKOPTS} | Opts]). -endif. + From 23e49c317dcb8f7581ebd98ea4f3db3e986e3577 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Thu, 16 Feb 2017 11:24:46 +0800 Subject: [PATCH 024/125] Improve the module and support statistics --- src/emqttd_client.erl | 210 ++++++++++++++++++++++++++---------------- 1 file changed, 131 insertions(+), 79 deletions(-) diff --git a/src/emqttd_client.erl b/src/emqttd_client.erl index 6f15bdf8c..1e4b3db4a 100644 --- a/src/emqttd_client.erl +++ b/src/emqttd_client.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -14,24 +14,36 @@ %% limitations under the License. %%-------------------------------------------------------------------- -%% @doc MQTT Client Connection +%% @doc MQTT/TCP Connection + -module(emqttd_client). -behaviour(gen_server). +-author("Feng Lee "). + -include("emqttd.hrl"). -include("emqttd_protocol.hrl"). -include("emqttd_internal.hrl"). +-import(proplists, [get_value/2, get_value/3]). + %% API Function Exports --export([start_link/2, session/1, info/1, kick/1, - set_rate_limit/2, get_rate_limit/1]). +-export([start_link/2]). + +%% Management and Monitor API +-export([info/1, stats/1, kick/1]). + +-export([set_rate_limit/2, get_rate_limit/1]). %% SUB/UNSUB Asynchronously. Called by plugins. -export([subscribe/2, unsubscribe/2]). +%% Get the session proc? +-export([session/1]). + %% gen_server Function Exports -export([init/1, handle_call/3, handle_cast/2, handle_info/2, code_change/3, terminate/2]). @@ -39,24 +51,25 @@ %% Client State -record(client_state, {connection, connname, peername, peerhost, peerport, await_recv, conn_state, rate_limit, parser_fun, - proto_state, packet_opts, keepalive, mountpoint}). + proto_state, packet_opts, keepalive, enable_stats, + stats_timer}). --define(INFO_KEYS, [peername, peerhost, peerport, await_recv, conn_state]). +-define(INFO_KEYS, [connname, peername, peerhost, peerport, await_recv, conn_state]). --define(SOCK_STATS, [recv_oct, recv_cnt, send_oct, send_cnt]). +-define(SOCK_STATS, [recv_oct, recv_cnt, send_oct, send_cnt, send_pend]). -define(LOG(Level, Format, Args, State), lager:Level("Client(~s): " ++ Format, [State#client_state.connname | Args])). -start_link(Connection, MqttEnv) -> - {ok, proc_lib:spawn_link(?MODULE, init, [[Connection, MqttEnv]])}. - -session(CPid) -> - gen_server:call(CPid, session, infinity). +start_link(Conn, Env) -> + {ok, proc_lib:spawn_link(?MODULE, init, [[Conn, Env]])}. info(CPid) -> gen_server:call(CPid, info, infinity). +stats(CPid) -> + gen_server:call(CPid, stats). + kick(CPid) -> gen_server:call(CPid, kick). @@ -67,42 +80,49 @@ get_rate_limit(Cpid) -> gen_server:call(Cpid, get_rate_limit). subscribe(CPid, TopicTable) -> - gen_server:cast(CPid, {subscribe, TopicTable}). + CPid ! {subscribe, TopicTable}. unsubscribe(CPid, Topics) -> - gen_server:cast(CPid, {unsubscribe, Topics}). + CPid ! {unsubscribe, Topics}. -init([OriginConn, MqttEnv]) -> - {ok, Connection} = OriginConn:wait(), +session(CPid) -> + gen_server2:call(CPid, session, infinity). + +%%-------------------------------------------------------------------- +%% gen_server Callbacks +%%-------------------------------------------------------------------- + +init([Conn0, Env]) -> + {ok, Conn} = Conn0:wait(), {PeerHost, PeerPort, PeerName} = - case Connection:peername() of + case Conn:peername() of {ok, Peer = {Host, Port}} -> {Host, Port, Peer}; {error, enotconn} -> - Connection:fast_close(), + Conn:fast_close(), exit(normal); {error, Reason} -> - Connection:fast_close(), + Conn:fast_close(), exit({shutdown, Reason}) end, ConnName = esockd_net:format(PeerName), Self = self(), - %% Send Packet... SendFun = fun(Packet) -> Data = emqttd_serializer:serialize(Packet), ?LOG(debug, "SEND ~p", [Data], #client_state{connname = ConnName}), emqttd_metrics:inc('bytes/sent', iolist_size(Data)), - try Connection:async_send(Data) of + try Conn:async_send(Data) of true -> ok catch error:Error -> Self ! {shutdown, Error} end end, - ParserFun = emqttd_parser:new(MqttEnv), - ProtoState = emqttd_protocol:init(PeerName, SendFun, MqttEnv), - RateLimit = proplists:get_value(rate_limit, Connection:opts()), - State = run_socket(#client_state{connection = Connection, + ParserFun = emqttd_parser:new(Env), + ProtoState = emqttd_protocol:init(PeerName, SendFun, Env), + RateLimit = get_value(rate_limit, Conn:opts()), + EnableStats = get_value(client_enable_stats, Env, false), + State = run_socket(#client_state{connection = Conn, connname = ConnName, peername = PeerName, peerhost = PeerHost, @@ -112,20 +132,21 @@ init([OriginConn, MqttEnv]) -> rate_limit = RateLimit, parser_fun = ParserFun, proto_state = ProtoState, - packet_opts = MqttEnv}), - IdleTimout = proplists:get_value(client_idle_timeout, MqttEnv, 30), - gen_server:enter_loop(?MODULE, [], State, timer:seconds(IdleTimout)). + packet_opts = Env, + enable_stats = EnableStats}), + IdleTimout = get_value(client_idle_timeout, Env, 30000), + gen_server:enter_loop(?MODULE, [], maybe_enable_stats(State), IdleTimout). -handle_call(session, _From, State = #client_state{proto_state = ProtoState}) -> - {reply, emqttd_protocol:session(ProtoState), State}; - -handle_call(info, _From, State = #client_state{connection = Connection, - proto_state = ProtoState}) -> - ClientInfo = ?record_to_proplist(client_state, State, ?INFO_KEYS), +handle_call(info, From, State = #client_state{proto_state = ProtoState}) -> ProtoInfo = emqttd_protocol:info(ProtoState), - {ok, SockStats} = Connection:getstat(?SOCK_STATS), - {reply, lists:append([ClientInfo, [{proto_info, ProtoInfo}, - {sock_stats, SockStats}]]), State}; + ClientInfo = ?record_to_proplist(client_state, State, ?INFO_KEYS), + {reply, Stats, _} = handle_call(stats, From, State), + {reply, lists:append([ClientInfo, ProtoInfo, Stats]), State}; + +handle_call(stats, _From, State = #client_state{proto_state = ProtoState}) -> + {reply, lists:append([emqttd_misc:proc_stats(), + emqttd_protocol:stats(ProtoState), + sock_stats(State)]), State}; handle_call(kick, _From, State) -> {stop, {shutdown, kick}, ok, State}; @@ -136,45 +157,56 @@ handle_call({set_rate_limit, Rl}, _From, State) -> handle_call(get_rate_limit, _From, State = #client_state{rate_limit = Rl}) -> {reply, Rl, State}; +handle_call(session, _From, State = #client_state{proto_state = ProtoState}) -> + {reply, emqttd_protocol:session(ProtoState), State}; + handle_call(Req, _From, State) -> ?UNEXPECTED_REQ(Req, State). -handle_cast({subscribe, TopicTable}, State) -> - with_proto_state(fun(ProtoState) -> - emqttd_protocol:handle({subscribe, TopicTable}, ProtoState) - end, State); - -handle_cast({unsubscribe, Topics}, State) -> - with_proto_state(fun(ProtoState) -> - emqttd_protocol:handle({unsubscribe, Topics}, ProtoState) - end, State); - handle_cast(Msg, State) -> ?UNEXPECTED_MSG(Msg, State). -handle_info(timeout, State) -> - shutdown(idle_timeout, State); +handle_info({subscribe, TopicTable}, State) -> + with_proto( + fun(ProtoState) -> + emqttd_protocol:subscribe(TopicTable, ProtoState) + end, State); -%% fix issue #535 -handle_info({shutdown, Error}, State) -> - shutdown(Error, State); +handle_info({unsubscribe, Topics}, State) -> + with_proto( + fun(ProtoState) -> + emqttd_protocol:unsubscribe(Topics, ProtoState) + end, State); %% Asynchronous SUBACK handle_info({suback, PacketId, GrantedQos}, State) -> - with_proto_state(fun(ProtoState) -> - Packet = ?SUBACK_PACKET(PacketId, GrantedQos), - emqttd_protocol:send(Packet, ProtoState) - end, State); + with_proto( + fun(ProtoState) -> + Packet = ?SUBACK_PACKET(PacketId, GrantedQos), + emqttd_protocol:send(Packet, ProtoState) + end, State); handle_info({deliver, Message}, State) -> - with_proto_state(fun(ProtoState) -> - emqttd_protocol:send(Message, ProtoState) - end, State); + with_proto( + fun(ProtoState) -> + emqttd_protocol:send(Message, ProtoState) + end, State); handle_info({redeliver, {?PUBREL, PacketId}}, State) -> - with_proto_state(fun(ProtoState) -> - emqttd_protocol:redeliver({?PUBREL, PacketId}, ProtoState) - end, State); + with_proto( + fun(ProtoState) -> + emqttd_protocol:pubrel(PacketId, ProtoState) + end, State); + +handle_info(timeout, State) -> + shutdown(idle_timeout, State); + +handle_info({timeout, _Timer, emit_stats}, State) -> + hibernate(maybe_enable_stats(emit_stats(State))); + +%% Fix issue #535 +handle_info({shutdown, Error}, State) -> + shutdown(Error, State); handle_info({shutdown, conflict, {ClientId, NewPid}}, State) -> ?LOG(warning, "clientid '~s' conflict with ~p", [ClientId, NewPid], State), @@ -193,26 +225,26 @@ handle_info({inet_async, _Sock, _Ref, {error, Reason}}, State) -> shutdown(Reason, State); handle_info({inet_reply, _Sock, ok}, State) -> - hibernate(State); + {noreply, State}; handle_info({inet_reply, _Sock, {error, Reason}}, State) -> shutdown(Reason, State); -handle_info({keepalive, start, Interval}, State = #client_state{connection = Connection}) -> +handle_info({keepalive, start, Interval}, State = #client_state{connection = Conn}) -> ?LOG(debug, "Keepalive at the interval of ~p", [Interval], State), StatFun = fun() -> - case Connection:getstat([recv_oct]) of + case Conn:getstat([recv_oct]) of {ok, [{recv_oct, RecvOct}]} -> {ok, RecvOct}; {error, Error} -> {error, Error} end end, KeepAlive = emqttd_keepalive:start(StatFun, Interval, {keepalive, check}), - hibernate(State#client_state{keepalive = KeepAlive}); + {noreply, stats_by_keepalive(State#client_state{keepalive = KeepAlive})}; handle_info({keepalive, check}, State = #client_state{keepalive = KeepAlive}) -> case emqttd_keepalive:check(KeepAlive) of {ok, KeepAlive1} -> - hibernate(State#client_state{keepalive = KeepAlive1}); + hibernate(emit_stats(State#client_state{keepalive = KeepAlive1})); {error, timeout} -> ?LOG(debug, "Keepalive timeout", [], State), shutdown(keepalive_timeout, State); @@ -224,10 +256,10 @@ handle_info({keepalive, check}, State = #client_state{keepalive = KeepAlive}) -> handle_info(Info, State) -> ?UNEXPECTED_INFO(Info, State). -terminate(Reason, #client_state{connection = Connection, +terminate(Reason, #client_state{connection = Conn, keepalive = KeepAlive, proto_state = ProtoState}) -> - Connection:fast_close(), + Conn:fast_close(), emqttd_keepalive:cancel(KeepAlive), case {ProtoState, Reason} of {undefined, _} -> @@ -245,10 +277,6 @@ code_change(_OldVsn, State, _Extra) -> %% Internal functions %%-------------------------------------------------------------------- -with_proto_state(Fun, State = #client_state{proto_state = ProtoState}) -> - {ok, ProtoState1} = Fun(ProtoState), - hibernate(State#client_state{proto_state = ProtoState1}). - %% Receive and parse tcp data received(<<>>, State) -> hibernate(State); @@ -258,7 +286,7 @@ received(Bytes, State = #client_state{parser_fun = ParserFun, proto_state = ProtoState}) -> case catch ParserFun(Bytes) of {more, NewParser} -> - noreply(run_socket(State#client_state{parser_fun = NewParser})); + {noreply, run_socket(State#client_state{parser_fun = NewParser})}; {ok, Packet, Rest} -> emqttd_metrics:received(Packet), case emqttd_protocol:received(Packet, ProtoState) of @@ -289,7 +317,7 @@ rate_limit(Size, State = #client_state{rate_limit = Rl}) -> {0, Rl1} -> run_socket(State#client_state{conn_state = running, rate_limit = Rl1}); {Pause, Rl1} -> - ?LOG(error, "Rate limiter pause for ~p", [Pause], State), + ?LOG(warning, "Rate limiter pause for ~p", [Pause], State), erlang:send_after(Pause, self(), activate_sock), State#client_state{conn_state = blocked, rate_limit = Rl1} end. @@ -298,12 +326,36 @@ run_socket(State = #client_state{conn_state = blocked}) -> State; run_socket(State = #client_state{await_recv = true}) -> State; -run_socket(State = #client_state{connection = Connection}) -> - Connection:async_recv(0, infinity), +run_socket(State = #client_state{connection = Conn}) -> + Conn:async_recv(0, infinity), State#client_state{await_recv = true}. -noreply(State) -> - {noreply, State}. +with_proto(Fun, State = #client_state{proto_state = ProtoState}) -> + {ok, ProtoState1} = Fun(ProtoState), + {noreply, State#client_state{proto_state = ProtoState1}}. + +maybe_enable_stats(State = #client_state{enable_stats = false}) -> + State; +maybe_enable_stats(State = #client_state{enable_stats = keepalive}) -> + State; +maybe_enable_stats(State = #client_state{enable_stats = Interval}) -> + State#client_state{stats_timer = emqttd_misc:start_timer(Interval, self(), emit_stats)}. + +stats_by_keepalive(State) -> + State#client_state{enable_stats = keepalive}. + +emit_stats(State = #client_state{enable_stats = false}) -> + State; +emit_stats(State = #client_state{proto_state = ProtoState}) -> + {reply, Stats, _} = handle_call(stats, undefined, State), + emqttd_stats:set_client_stats(emqttd_protocol:clientid(ProtoState), Stats), + State. + +sock_stats(#client_state{connection = Conn}) -> + case Conn:getstat(?SOCK_STATS) of + {ok, Ss} -> Ss; + {error, _} -> [] + end. hibernate(State) -> {noreply, State, hibernate}. From 90ff296ebeac2d20400cae05da17317b36e128e5 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Thu, 16 Feb 2017 11:25:10 +0800 Subject: [PATCH 025/125] Improve the module and support statistics --- src/emqttd_ws_client.erl | 176 +++++++++++++++++++++++++-------------- 1 file changed, 112 insertions(+), 64 deletions(-) diff --git a/src/emqttd_ws_client.erl b/src/emqttd_ws_client.erl index bd416fa27..3641a98a8 100644 --- a/src/emqttd_ws_client.erl +++ b/src/emqttd_ws_client.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -18,50 +18,64 @@ -behaviour(gen_server). +-author("Feng Lee "). + -include("emqttd.hrl"). -include("emqttd_protocol.hrl"). %% API Exports --export([start_link/4, session/1, info/1, kick/1]). +-export([start_link/4]). + +%% Management and Monitor API +-export([info/1, stats/1, kick/1]). %% SUB/UNSUB Asynchronously -export([subscribe/2, unsubscribe/2]). +%% Get the session proc? +-export([session/1]). + %% gen_server Function Exports -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). %% WebSocket Client State --record(wsclient_state, {ws_pid, peer, connection, proto_state, keepalive}). +-record(wsclient_state, {ws_pid, peer, connection, proto_state, keepalive, + enable_stats, stats_timer}). + +-define(SOCK_STATS, [recv_oct, recv_cnt, send_oct, send_cnt, send_pend]). -define(WSLOG(Level, Peer, Format, Args), lager:Level("WsClient(~s): " ++ Format, [Peer | Args])). %% @doc Start WebSocket Client. -start_link(MqttEnv, WsPid, Req, ReplyChannel) -> - gen_server:start_link(?MODULE, [MqttEnv, WsPid, Req, ReplyChannel], []). - -session(CPid) -> - gen_server:call(CPid, session, infinity). +start_link(Env, WsPid, Req, ReplyChannel) -> + gen_server:start_link(?MODULE, [Env, WsPid, Req, ReplyChannel], []). info(CPid) -> - gen_server:call(CPid, info, infinity). + gen_server:call(CPid, info). + +stats(CPid) -> + gen_server:call(CPid, stats). kick(CPid) -> gen_server:call(CPid, kick). subscribe(CPid, TopicTable) -> - gen_server:cast(CPid, {subscribe, TopicTable}). + CPid ! {subscribe, TopicTable}. unsubscribe(CPid, Topics) -> - gen_server:cast(CPid, {unsubscribe, Topics}). + CPid ! {unsubscribe, Topics}. + +session(CPid) -> + gen_server:call(CPid, session). %%-------------------------------------------------------------------- %% gen_server Callbacks %%-------------------------------------------------------------------- -init([MqttEnv, WsPid, Req, ReplyChannel]) -> +init([Env, WsPid, Req, ReplyChannel]) -> process_flag(trap_exit, true), true = link(WsPid), {ok, Peername} = Req:get(peername), @@ -73,45 +87,41 @@ init([MqttEnv, WsPid, Req, ReplyChannel]) -> emqttd_metrics:inc('bytes/sent', iolist_size(Data)), ReplyChannel({binary, Data}) end, + EnableStats = proplists:get_value(client_enable_stats, Env, false), ProtoState = emqttd_protocol:init(Peername, SendFun, - [{ws_initial_headers, Headers} | MqttEnv]), - {ok, #wsclient_state{ws_pid = WsPid, peer = Req:get(peer), - connection = Req:get(connection), - proto_state = ProtoState}, idle_timeout(MqttEnv)}. + [{ws_initial_headers, Headers} | Env]), + {ok, maybe_enable_stats(#wsclient_state{ws_pid = WsPid, + peer = Req:get(peer), + connection = Req:get(connection), + proto_state = ProtoState, + enable_stats = EnableStats}), + proplists:get_value(client_idle_timeout, Env, 30000)}. -idle_timeout(MqttEnv) -> - timer:seconds(proplists:get_value(client_idle_timeout, MqttEnv, 10)). +handle_call(info, From, State = #wsclient_state{peer = Peer, proto_state = ProtoState}) -> + Info = [{websocket, true}, {peer, Peer} | emqttd_protocol:info(ProtoState)], + {reply, Stats, _} = handle_call(stats, From, State), + {reply, lists:append(Info, Stats), State}; -handle_call(session, _From, State = #wsclient_state{proto_state = ProtoState}) -> - {reply, emqttd_protocol:session(ProtoState), State}; - -handle_call(info, _From, State = #wsclient_state{peer = Peer, - proto_state = ProtoState}) -> - ProtoInfo = emqttd_protocol:info(ProtoState), - {reply, [{websocket, true}, {peer, Peer}| ProtoInfo], State}; +handle_call(stats, _From, State = #wsclient_state{proto_state = ProtoState}) -> + {reply, lists:append([emqttd_misc:proc_stats(), + wsock_stats(State), + emqttd_protocol:stats(ProtoState)]), State}; handle_call(kick, _From, State) -> {stop, {shutdown, kick}, ok, State}; +handle_call(session, _From, State = #wsclient_state{proto_state = ProtoState}) -> + {reply, emqttd_protocol:session(ProtoState), State}; + handle_call(Req, _From, State = #wsclient_state{peer = Peer}) -> - ?WSLOG(critical, Peer, "Unexpected request: ~p", [Req]), + ?WSLOG(error, Peer, "Unexpected request: ~p", [Req]), {reply, {error, unsupported_request}, State}. -handle_cast({subscribe, TopicTable}, State) -> - with_proto_state(fun(ProtoState) -> - emqttd_protocol:handle({subscribe, TopicTable}, ProtoState) - end, State); - -handle_cast({unsubscribe, Topics}, State) -> - with_proto_state(fun(ProtoState) -> - emqttd_protocol:handle({unsubscribe, Topics}, ProtoState) - end, State); - handle_cast({received, Packet}, State = #wsclient_state{peer = Peer, proto_state = ProtoState}) -> emqttd_metrics:received(Packet), case emqttd_protocol:received(Packet, ProtoState) of {ok, ProtoState1} -> - noreply(State#wsclient_state{proto_state = ProtoState1}); + {noreply, State#wsclient_state{proto_state = ProtoState1}, hibernate}; {error, Error} -> ?WSLOG(error, Peer, "Protocol error - ~p", [Error]), shutdown(Error, State); @@ -122,28 +132,46 @@ handle_cast({received, Packet}, State = #wsclient_state{peer = Peer, proto_state end; handle_cast(Msg, State = #wsclient_state{peer = Peer}) -> - ?WSLOG(critical, Peer, "Unexpected msg: ~p", [Msg]), - noreply(State). + ?WSLOG(error, Peer, "Unexpected msg: ~p", [Msg]), + {noreply, State}. + +handle_info({subscribe, TopicTable}, State) -> + with_proto( + fun(ProtoState) -> + emqttd_protocol:subscribe(TopicTable, ProtoState) + end, State); + +handle_info({unsubscribe, Topics}, State) -> + with_proto( + fun(ProtoState) -> + emqttd_protocol:unsubscribe(Topics, ProtoState) + end, State); + +handle_info({suback, PacketId, GrantedQos}, State) -> + with_proto( + fun(ProtoState) -> + Packet = ?SUBACK_PACKET(PacketId, GrantedQos), + emqttd_protocol:send(Packet, ProtoState) + end, State); + +handle_info({deliver, Message}, State) -> + with_proto( + fun(ProtoState) -> + emqttd_protocol:send(Message, ProtoState) + end, State); + +handle_info({redeliver, {?PUBREL, PacketId}}, State) -> + with_proto( + fun(ProtoState) -> + emqttd_protocol:pubrel(PacketId, ProtoState) + end, State); + +handle_info({timeout, _Timer, emit_stats}, State) -> + {noreply, maybe_enable_stats(emit_stats(State)), hibernate}; handle_info(timeout, State) -> shutdown(idle_timeout, State); -handle_info({suback, PacketId, GrantedQos}, State) -> - with_proto_state(fun(ProtoState) -> - Packet = ?SUBACK_PACKET(PacketId, GrantedQos), - emqttd_protocol:send(Packet, ProtoState) - end, State); - -handle_info({deliver, Message}, State) -> - with_proto_state(fun(ProtoState) -> - emqttd_protocol:send(Message, ProtoState) - end, State); - -handle_info({redeliver, {?PUBREL, PacketId}}, State) -> - with_proto_state(fun(ProtoState) -> - emqttd_protocol:redeliver({?PUBREL, PacketId}, ProtoState) - end, State); - handle_info({shutdown, conflict, {ClientId, NewPid}}, State = #wsclient_state{peer = Peer}) -> ?WSLOG(warning, Peer, "clientid '~s' conflict with ~p", [ClientId, NewPid]), shutdown(conflict, State); @@ -157,13 +185,13 @@ handle_info({keepalive, start, Interval}, State = #wsclient_state{peer = Peer, c end end, KeepAlive = emqttd_keepalive:start(StatFun, Interval, {keepalive, check}), - noreply(State#wsclient_state{keepalive = KeepAlive}); + {noreply, stats_by_keepalive(State#wsclient_state{keepalive = KeepAlive})}; handle_info({keepalive, check}, State = #wsclient_state{peer = Peer, keepalive = KeepAlive}) -> case emqttd_keepalive:check(KeepAlive) of {ok, KeepAlive1} -> - noreply(State#wsclient_state{keepalive = KeepAlive1}); + {noreply, emit_stats(State#wsclient_state{keepalive = KeepAlive1}), hibernate}; {error, timeout} -> ?WSLOG(debug, Peer, "Keepalive Timeout!", []), shutdown(keepalive_timeout, State); @@ -180,8 +208,8 @@ handle_info({'EXIT', WsPid, Reason}, State = #wsclient_state{peer = Peer, ws_pid shutdown(Reason, State); handle_info(Info, State = #wsclient_state{peer = Peer}) -> - ?WSLOG(critical, Peer, "Unexpected Info: ~p", [Info]), - noreply(State). + ?WSLOG(error, Peer, "Unexpected Info: ~p", [Info]), + {noreply, State}. terminate(Reason, #wsclient_state{proto_state = ProtoState, keepalive = KeepAlive}) -> emqttd_keepalive:cancel(KeepAlive), @@ -199,12 +227,32 @@ code_change(_OldVsn, State, _Extra) -> %% Internal functions %%-------------------------------------------------------------------- -with_proto_state(Fun, State = #wsclient_state{proto_state = ProtoState}) -> - {ok, ProtoState1} = Fun(ProtoState), - noreply(State#wsclient_state{proto_state = ProtoState1}). +maybe_enable_stats(State = #wsclient_state{enable_stats = false}) -> + State; +maybe_enable_stats(State = #wsclient_state{enable_stats = keepalive}) -> + State; +maybe_enable_stats(State = #wsclient_state{enable_stats = Interval}) -> + State#wsclient_state{stats_timer = emqttd_misc:start_timer(Interval, self(), emit_stats)}. -noreply(State) -> - {noreply, State, hibernate}. +stats_by_keepalive(State) -> + State#wsclient_state{enable_stats = keepalive}. + +emit_stats(State = #wsclient_state{enable_stats = false}) -> + State; +emit_stats(State = #wsclient_state{proto_state = ProtoState}) -> + {reply, Stats, _} = handle_call(stats, undefined, State), + emqttd_stats:set_client_stats(emqttd_protocol:clientid(ProtoState), Stats), + State. + +wsock_stats(#wsclient_state{connection = Conn}) -> + case Conn:getstat(?SOCK_STATS) of + {ok, Ss} -> Ss; + {error, _} -> [] + end. + +with_proto(Fun, State = #wsclient_state{proto_state = ProtoState}) -> + {ok, ProtoState1} = Fun(ProtoState), + {noreply, State#wsclient_state{proto_state = ProtoState1}}. shutdown(Reason, State) -> stop({shutdown, Reason}, State). From a54076cf0aa9cca094d27e3505d29bdc73011165 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Thu, 16 Feb 2017 11:25:44 +0800 Subject: [PATCH 026/125] Change the 'Env' --- src/emqttd_ws_client_sup.erl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/emqttd_ws_client_sup.erl b/src/emqttd_ws_client_sup.erl index d01055115..60e1461b7 100644 --- a/src/emqttd_ws_client_sup.erl +++ b/src/emqttd_ws_client_sup.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -37,8 +37,9 @@ start_client(WsPid, Req, ReplyChannel) -> %%-------------------------------------------------------------------- %% Supervisor callbacks %%-------------------------------------------------------------------- + init([]) -> - {ok, Env} = emqttd:env(protocol), + Env = lists:append(emqttd:env(client), emqttd:env(protocol)), {ok, {{simple_one_for_one, 0, 1}, [{ws_client, {emqttd_ws_client, start_link, [Env]}, temporary, 5000, worker, [emqttd_ws_client]}]}}. From 5a49196a07941f99cffc645ae05036fe94c6babd Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Thu, 16 Feb 2017 11:28:35 +0800 Subject: [PATCH 027/125] Update copyright info and format code --- src/emqttd_cm_sup.erl | 5 ++++- src/emqttd_parser.erl | 12 +++++++----- src/emqttd_plugins.erl | 4 +++- src/emqttd_pmon.erl | 11 +++++++---- src/emqttd_sm_sup.erl | 5 ++++- src/emqttd_ws.erl | 4 +++- 6 files changed, 28 insertions(+), 13 deletions(-) diff --git a/src/emqttd_cm_sup.erl b/src/emqttd_cm_sup.erl index dfa623cc3..fc01ea649 100644 --- a/src/emqttd_cm_sup.erl +++ b/src/emqttd_cm_sup.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -15,10 +15,13 @@ %%-------------------------------------------------------------------- %% @doc Client Manager Supervisor. + -module(emqttd_cm_sup). -behaviour(supervisor). +-author("Feng Lee "). + -include("emqttd.hrl"). %% API diff --git a/src/emqttd_parser.erl b/src/emqttd_parser.erl index d6c09c42e..669f4aab2 100644 --- a/src/emqttd_parser.erl +++ b/src/emqttd_parser.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -17,6 +17,8 @@ %% @doc MQTT Packet Parser -module(emqttd_parser). +-author("Feng Lee "). + -include("emqttd.hrl"). -include("emqttd_protocol.hrl"). @@ -26,9 +28,9 @@ -record(mqtt_packet_limit, {max_packet_size}). --type option() :: {atom(), any()}. +-type(option() :: {atom(), any()}). --type parser() :: fun( (binary()) -> any() ). +-type(parser() :: fun( (binary()) -> any() )). %% @doc Initialize a parser -spec(new(Opts :: [option()]) -> parser()). @@ -86,7 +88,7 @@ parse_frame(Bin, #mqtt_packet_header{type = Type, qos = Qos} = Header, Length) WillRetain : 1, WillQos : 2, WillFlag : 1, - CleanSession : 1, + CleanSess : 1, _Reserved : 1, KeepAlive : 16/big, Rest3/binary>> = Rest2, @@ -104,7 +106,7 @@ parse_frame(Bin, #mqtt_packet_header{type = Type, qos = Qos} = Header, Length) will_retain = bool(WillRetain), will_qos = WillQos, will_flag = bool(WillFlag), - clean_sess = bool(CleanSession), + clean_sess = bool(CleanSess), keep_alive = KeepAlive, client_id = ClientId, will_topic = WillTopic, diff --git a/src/emqttd_plugins.erl b/src/emqttd_plugins.erl index 78b543440..f126f631c 100644 --- a/src/emqttd_plugins.erl +++ b/src/emqttd_plugins.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -16,6 +16,8 @@ -module(emqttd_plugins). +-author("Feng Lee "). + -include("emqttd.hrl"). -export([init/0]). diff --git a/src/emqttd_pmon.erl b/src/emqttd_pmon.erl index e5f286eea..ebe691ad4 100644 --- a/src/emqttd_pmon.erl +++ b/src/emqttd_pmon.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2016-2017 Feng Lee . All Rights Reserved. +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -18,11 +18,14 @@ -author("Feng Lee "). --type(pmon() :: {?MODULE, map()}). - -export([new/0, monitor/2, demonitor/2, erase/2]). -new() -> {?MODULE, [maps:new()]}. +-type(pmon() :: {?MODULE, map()}). + +-export_type([pmon/0]). + +new() -> + {?MODULE, [maps:new()]}. -spec(monitor(pid(), pmon()) -> pmon()). monitor(Pid, PM = {?MODULE, [M]}) -> diff --git a/src/emqttd_sm_sup.erl b/src/emqttd_sm_sup.erl index c43f89f80..efabadb96 100644 --- a/src/emqttd_sm_sup.erl +++ b/src/emqttd_sm_sup.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -15,10 +15,13 @@ %%-------------------------------------------------------------------- %% @doc Session Manager Supervisor. + -module(emqttd_sm_sup). -behaviour(supervisor). +-author("Feng Lee "). + -include("emqttd.hrl"). -define(SM, emqttd_sm). diff --git a/src/emqttd_ws.erl b/src/emqttd_ws.erl index a5b3b344f..8c483344e 100644 --- a/src/emqttd_ws.erl +++ b/src/emqttd_ws.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -16,6 +16,8 @@ -module(emqttd_ws). +-author("Feng Lee "). + -export([handle_request/1, ws_loop/3]). %% WebSocket Loop State From 20216670216399accd330e9296176ff16d2304ad Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Thu, 16 Feb 2017 11:29:55 +0800 Subject: [PATCH 028/125] Add 'local_session/0 function, change reg_session/3 unreg_session/1 functions --- src/emqttd_sm.erl | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/emqttd_sm.erl b/src/emqttd_sm.erl index 704242b2d..02dfdb879 100644 --- a/src/emqttd_sm.erl +++ b/src/emqttd_sm.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -14,9 +14,10 @@ %% limitations under the License. %%-------------------------------------------------------------------- -%% @doc Session Manager -module(emqttd_sm). +-author("Feng Lee "). + -behaviour(gen_server2). -include("emqttd.hrl"). @@ -32,10 +33,12 @@ %% API Function Exports -export([start_link/2]). --export([start_session/2, lookup_session/1, reg_session/3, unreg_session/1]). +-export([start_session/2, lookup_session/1, register_session/3, unregister_session/1]). -export([dispatch/3]). +-export([local_sessions/0]). + %% gen_server Function Exports -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). @@ -91,13 +94,13 @@ lookup_session(ClientId) -> end. %% @doc Register a session with info. --spec(reg_session(binary(), boolean(), [tuple()]) -> true). -reg_session(ClientId, CleanSess, Properties) -> +-spec(register_session(binary(), boolean(), [tuple()]) -> true). +register_session(ClientId, CleanSess, Properties) -> ets:insert(mqtt_local_session, {ClientId, self(), CleanSess, Properties}). %% @doc Unregister a session. --spec(unreg_session(binary()) -> true). -unreg_session(ClientId) -> +-spec(unregister_session(binary()) -> true). +unregister_session(ClientId) -> ets:delete(mqtt_local_session, ClientId). dispatch(ClientId, Topic, Msg) -> @@ -110,8 +113,12 @@ dispatch(ClientId, Topic, Msg) -> call(SM, Req) -> gen_server2:call(SM, Req, ?TIMEOUT). %%infinity). +%% @doc for debug. +local_sessions() -> + ets:tab2list(mqtt_local_session). + %%-------------------------------------------------------------------- -%% gen_server callbacks +%% gen_server Callbacks %%-------------------------------------------------------------------- init([Pool, Id]) -> @@ -171,6 +178,7 @@ handle_info({'DOWN', MRef, process, DownPid, _Reason}, State) -> [] -> ok; [Sess = #mqtt_session{sess_pid = DownPid}] -> + emqttd_stats:del_session_stats(ClientId), mnesia:delete_object(mqtt_session, Sess, write); [_Sess] -> ok @@ -208,7 +216,7 @@ create_session({CleanSess, {ClientId, Username}, ClientPid}, State) -> create_session(CleanSess, {ClientId, Username}, ClientPid) -> case emqttd_session_sup:start_session(CleanSess, {ClientId, Username}, ClientPid) of {ok, SessPid} -> - Session = #mqtt_session{client_id = ClientId, sess_pid = SessPid, persistent = not CleanSess}, + Session = #mqtt_session{client_id = ClientId, sess_pid = SessPid, clean_sess = CleanSess}, case insert_session(Session) of {aborted, {conflict, ConflictPid}} -> %% Conflict with othe node? From 6b22fb0a0c71ae0386e964715e8f606c2d2baa4c Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Thu, 16 Feb 2017 11:30:43 +0800 Subject: [PATCH 029/125] Remove the 'DOWN' client from emqttd_stats --- src/emqttd_cm.erl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/emqttd_cm.erl b/src/emqttd_cm.erl index 6b40a480f..2e57ebe5b 100644 --- a/src/emqttd_cm.erl +++ b/src/emqttd_cm.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -15,10 +15,13 @@ %%-------------------------------------------------------------------- %% @doc MQTT Client Manager + -module(emqttd_cm). -behaviour(gen_server2). +-author("Feng Lee "). + -include("emqttd.hrl"). -include("emqttd_internal.hrl"). @@ -120,6 +123,7 @@ handle_info({'DOWN', MRef, process, DownPid, _Reason}, State) -> {ok, {ClientId, DownPid}} -> case lookup_proc(ClientId) of DownPid -> + emqttd_stats:del_client_stats(ClientId), ets:delete(mqtt_client, ClientId); _ -> ignore From a5ac32b49b1b60a76425b708d51104362587567f Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Thu, 16 Feb 2017 11:31:46 +0800 Subject: [PATCH 030/125] add 'packets/puback/missed', 'packets/pubrec/missed', 'packets/pubrel/missed', 'packets/pubcomp/missed' metrics --- src/emqttd_metrics.erl | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/src/emqttd_metrics.erl b/src/emqttd_metrics.erl index 6f7a53f5a..2c0e42ea0 100644 --- a/src/emqttd_metrics.erl +++ b/src/emqttd_metrics.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -14,11 +14,12 @@ %% limitations under the License. %%-------------------------------------------------------------------- -%% @doc emqttd metrics. responsible for collecting broker metrics. -module(emqttd_metrics). -behaviour(gen_server). +-author("Feng Lee "). + -include("emqttd.hrl"). -include("emqttd_protocol.hrl"). @@ -37,7 +38,7 @@ -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). --record(state, {tick_tref}). +-record(state, {tick}). -define(METRIC_TAB, mqtt_metric). @@ -57,12 +58,16 @@ {counter, 'packets/publish/sent'}, % PUBLISH packets sent {counter, 'packets/puback/received'}, % PUBACK packets received {counter, 'packets/puback/sent'}, % PUBACK packets sent + {counter, 'packets/puback/missed'}, % PUBACK packets missed {counter, 'packets/pubrec/received'}, % PUBREC packets received {counter, 'packets/pubrec/sent'}, % PUBREC packets sent + {counter, 'packets/pubrec/missed'}, % PUBREC packets missed {counter, 'packets/pubrel/received'}, % PUBREL packets received {counter, 'packets/pubrel/sent'}, % PUBREL packets sent + {counter, 'packets/pubrel/missed'}, % PUBREL packets missed {counter, 'packets/pubcomp/received'}, % PUBCOMP packets received {counter, 'packets/pubcomp/sent'}, % PUBCOMP packets sent + {counter, 'packets/pubcomp/missed'}, % PUBCOMP packets missed {counter, 'packets/subscribe'}, % SUBSCRIBE Packets received {counter, 'packets/suback'}, % SUBACK packets sent {counter, 'packets/unsubscribe'}, % UNSUBSCRIBE Packets received @@ -74,14 +79,15 @@ %% Messages sent and received of broker -define(SYSTOP_MESSAGES, [ - {counter, 'messages/received'}, % Messages received - {counter, 'messages/sent'}, % Messages sent - {counter, 'messages/qos0/received'}, % Messages received - {counter, 'messages/qos0/sent'}, % Messages sent - {counter, 'messages/qos1/received'}, % Messages received - {counter, 'messages/qos1/sent'}, % Messages sent - {counter, 'messages/qos2/received'}, % Messages received - {counter, 'messages/qos2/sent'}, % Messages sent + {counter, 'messages/received'}, % All Messages received + {counter, 'messages/sent'}, % All Messages sent + {counter, 'messages/qos0/received'}, % QoS0 Messages received + {counter, 'messages/qos0/sent'}, % QoS0 Messages sent + {counter, 'messages/qos1/received'}, % QoS1 Messages received + {counter, 'messages/qos1/sent'}, % QoS1 Messages sent + {counter, 'messages/qos2/received'}, % QoS2 Messages received + {counter, 'messages/qos2/sent'}, % QoS2 Messages sent + {counter, 'messages/qos2/dropped'}, % QoS2 Messages dropped {gauge, 'messages/retained'}, % Messagea retained {counter, 'messages/dropped'} % Messages dropped ]). @@ -138,7 +144,7 @@ qos_received(?QOS_2) -> sent(?PUBLISH_PACKET(_Qos, <<"$SYS/", _/binary>>, _, _)) -> ignore; sent(Packet) -> - emqttd_metrics:inc('packets/sent'), + inc('packets/sent'), sent1(Packet). sent1(?PUBLISH_PACKET(Qos, _PktId)) -> inc('packets/publish/sent'), @@ -245,7 +251,7 @@ init([]) -> % $SYS Topics for metrics % [ok = emqttd:create(topic, metric_topic(Topic)) || {_, Topic} <- Metrics], % Tick to publish metrics - {ok, #state{tick_tref = emqttd_broker:start_tick(tick)}, hibernate}. + {ok, #state{tick = emqttd_broker:start_tick(tick)}, hibernate}. handle_call(_Req, _From, State) -> {reply, error, State}. @@ -261,7 +267,7 @@ handle_info(tick, State) -> handle_info(_Info, State) -> {noreply, State}. -terminate(_Reason, #state{tick_tref = TRef}) -> +terminate(_Reason, #state{tick = TRef}) -> emqttd_broker:stop_tick(TRef). code_change(_OldVsn, State, _Extra) -> From 67566ca372df6a06dac09cdb673382dbe4a65ab0 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Thu, 16 Feb 2017 11:32:24 +0800 Subject: [PATCH 031/125] Add dropped/1 function --- src/emqttd_mqueue.erl | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/emqttd_mqueue.erl b/src/emqttd_mqueue.erl index 53f37206f..9abcc6843 100644 --- a/src/emqttd_mqueue.erl +++ b/src/emqttd_mqueue.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -43,35 +43,33 @@ -module(emqttd_mqueue). +-author("Feng Lee "). + -include("emqttd.hrl"). -include("emqttd_protocol.hrl"). -import(proplists, [get_value/3]). --export([new/3, type/1, name/1, is_empty/1, len/1, max_len/1, in/2, out/1, stats/1]). +-export([new/3, type/1, name/1, is_empty/1, len/1, max_len/1, in/2, out/1, + dropped/1, stats/1]). -define(LOW_WM, 0.2). -define(HIGH_WM, 0.6). --type priority() :: {iolist(), pos_integer()}. +-type(priority() :: {iolist(), pos_integer()}). --type option() :: {type, simple | priority} +-type(option() :: {type, simple | priority} | {max_length, pos_integer() | infinity} | {priority, list(priority())} - | {low_watermark, float()} %% Low watermark - | {high_watermark, float()} %% High watermark - | {queue_qos0, boolean()}. %% Queue Qos0? + | {low_watermark, float()} %% Low watermark + | {high_watermark, float()} %% High watermark + | {queue_qos0, boolean()}). %% Queue Qos0? --type mqueue_option() :: {max_length, pos_integer()} %% Max queue length - | {low_watermark, float()} %% Low watermark - | {high_watermark, float()} %% High watermark - | {queue_qos0, boolean()}. %% Queue Qos0 - --type stat() :: {max_len, infinity | pos_integer()} +-type(stat() :: {max_len, infinity | pos_integer()} | {len, non_neg_integer()} - | {dropped, non_neg_integer()}. + | {dropped, non_neg_integer()}). -record(mqueue, {type :: simple | priority, name, q :: queue:queue() | priority_queue:q(), @@ -83,12 +81,12 @@ qos0 = false, dropped = 0, alarm_fun}). --type mqueue() :: #mqueue{}. +-type(mqueue() :: #mqueue{}). -export_type([mqueue/0, priority/0, option/0]). %% @doc New Queue. --spec(new(iolist(), list(mqueue_option()), fun()) -> mqueue()). +-spec(new(iolist(), list(option()), fun()) -> mqueue()). new(Name, Opts, AlarmFun) -> Type = get_value(type, Opts, simple), MaxLen = get_value(max_length, Opts, infinity), @@ -141,6 +139,10 @@ len(#mqueue{type = priority, q = Q}) -> priority_queue:len(Q). max_len(#mqueue{max_len= MaxLen}) -> MaxLen. +%% @doc Dropped of the mqueue +-spec(dropped(mqueue()) -> non_neg_integer()). +dropped(#mqueue{dropped = Dropped}) -> Dropped. + %% @doc Stats of the mqueue -spec(stats(mqueue()) -> [stat()]). stats(#mqueue{type = Type, q = Q, max_len = MaxLen, len = Len, dropped = Dropped}) -> @@ -208,14 +210,12 @@ maybe_set_alarm(MQ = #mqueue{name = Name, len = Len, high_wm = HighWM, alarm_fun title = io_lib:format("Queue ~s high-water mark", [Name]), summary = io_lib:format("queue len ~p > high_watermark ~p", [Len, HighWM])}, MQ#mqueue{alarm_fun = AlarmFun(alert, Alarm)}; - maybe_set_alarm(MQ) -> MQ. maybe_clear_alarm(MQ = #mqueue{name = Name, len = Len, low_wm = LowWM, alarm_fun = AlarmFun}) when Len < LowWM -> MQ#mqueue{alarm_fun = AlarmFun(clear, list_to_binary(["queue_high_watermark.", Name]))}; - maybe_clear_alarm(MQ) -> MQ. From bbbfafb759e1bcf36e419e482ac8422e5b4ed629 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Thu, 16 Feb 2017 11:33:27 +0800 Subject: [PATCH 032/125] Update protocol_name/1, type_name/1 functions for MQTT 5 --- src/emqttd_protocol.erl | 84 +++++++++++++++++++++++++---------------- 1 file changed, 52 insertions(+), 32 deletions(-) diff --git a/src/emqttd_protocol.erl b/src/emqttd_protocol.erl index f0b877dc5..a9cc37778 100644 --- a/src/emqttd_protocol.erl +++ b/src/emqttd_protocol.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -16,6 +16,8 @@ -module(emqttd_protocol). +-author("Feng Lee "). + -include("emqttd.hrl"). -include("emqttd_protocol.hrl"). @@ -25,9 +27,11 @@ -import(proplists, [get_value/2, get_value/3]). %% API --export([init/3, info/1, clientid/1, client/1, session/1]). +-export([init/3, info/1, stats/1, clientid/1, client/1, session/1]). --export([received/2, handle/2, send/2, redeliver/2, shutdown/2]). +-export([subscribe/2, unsubscribe/2, pubrel/2, shutdown/2]). + +-export([received/2, send/2]). -export([process/2]). @@ -39,17 +43,20 @@ session, ws_initial_headers, %% Headers from first HTTP request for websocket client connected_at}). --type proto_state() :: #proto_state{}. +-type(proto_state() :: #proto_state{}). -define(INFO_KEYS, [client_id, username, clean_sess, proto_ver, proto_name, keepalive, will_msg, ws_initial_headers, connected_at]). +-define(STATS_KEYS, [recv_pkt, recv_msg, send_pkt, send_msg]). + -define(LOG(Level, Format, Args, State), lager:Level([{client, State#proto_state.client_id}], "Client(~s@~s): " ++ Format, [State#proto_state.client_id, esockd_net:format(State#proto_state.peername) | Args])). %% @doc Init protocol init(Peername, SendFun, Opts) -> + lists:foreach(fun(K) -> put(K, 0) end, ?STATS_KEYS), MaxLen = get_value(max_clientid_len, Opts, ?MAX_CLIENTID_LEN), WsInitialHeaders = get_value(ws_initial_headers, Opts), #proto_state{peername = Peername, @@ -61,6 +68,9 @@ init(Peername, SendFun, Opts) -> info(ProtoState) -> ?record_to_proplist(proto_state, ProtoState, ?INFO_KEYS). +stats(_ProtoState) -> + [{K, get(K)} || K <- ?STATS_KEYS]. + clientid(#proto_state{client_id = ClientId}) -> ClientId. @@ -115,22 +125,22 @@ received(Packet = ?PACKET(_Type), State) -> {error, Reason, State} end. -handle({subscribe, RawTopicTable}, ProtoState = #proto_state{client_id = ClientId, - username = Username, - session = Session}) -> +subscribe(RawTopicTable, ProtoState = #proto_state{client_id = ClientId, + username = Username, + session = Session}) -> TopicTable = parse_topic_table(RawTopicTable), - case emqttd:run_hooks('client.subscribe', [ClientId, Username], TopicTable) of + case emqttd_hooks:run('client.subscribe', [ClientId, Username], TopicTable) of {ok, TopicTable1} -> emqttd_session:subscribe(Session, TopicTable1); {stop, _} -> ok end, - {ok, ProtoState}; + {ok, ProtoState}. -handle({unsubscribe, RawTopics}, ProtoState = #proto_state{client_id = ClientId, - username = Username, - session = Session}) -> - case emqttd:run_hooks('client.unsubscribe', [ClientId, Username], parse_topics(RawTopics)) of +unsubscribe(RawTopics, ProtoState = #proto_state{client_id = ClientId, + username = Username, + session = Session}) -> + case emqttd_hooks:run('client.unsubscribe', [ClientId, Username], parse_topics(RawTopics)) of {ok, TopicTable} -> emqttd_session:unsubscribe(Session, TopicTable); {stop, _} -> @@ -138,6 +148,9 @@ handle({unsubscribe, RawTopics}, ProtoState = #proto_state{client_id = ClientId, end, {ok, ProtoState}. +%% @doc Send PUBREL +pubrel(PacketId, State) -> send(?PUBREL_PACKET(PacketId), State). + process(Packet = ?CONNECT_PACKET(Var), State0) -> #mqtt_packet_connect{proto_ver = ProtoVer, @@ -187,7 +200,7 @@ process(Packet = ?CONNECT_PACKET(Var), State0) -> {ReturnCode, false, State1} end, %% Run hooks - emqttd:run_hooks('client.connected', [ReturnCode1], client(State3)), + emqttd_hooks:run('client.connected', [ReturnCode1], client(State3)), %% Send connack send(?CONNACK_PACKET(ReturnCode1, sp(SessPresent)), State3), %% stop if authentication failure @@ -220,8 +233,11 @@ process(?SUBSCRIBE_PACKET(PacketId, []), State) -> send(?SUBACK_PACKET(PacketId, []), State); %% TODO: refactor later... -process(?SUBSCRIBE_PACKET(PacketId, RawTopicTable), State = #proto_state{session = Session, - client_id = ClientId, username = Username, is_superuser = IsSuperuser}) -> +process(?SUBSCRIBE_PACKET(PacketId, RawTopicTable), + State = #proto_state{session = Session, + client_id = ClientId, + username = Username, + is_superuser = IsSuperuser}) -> Client = client(State), TopicTable = parse_topic_table(RawTopicTable), AllowDenies = if IsSuperuser -> []; @@ -232,7 +248,7 @@ process(?SUBSCRIBE_PACKET(PacketId, RawTopicTable), State = #proto_state{session ?LOG(error, "Cannot SUBSCRIBE ~p for ACL Deny", [TopicTable], State), send(?SUBACK_PACKET(PacketId, [16#80 || _ <- TopicTable]), State); false -> - case emqttd:run_hooks('client.subscribe', [ClientId, Username], TopicTable) of + case emqttd_hooks:run('client.subscribe', [ClientId, Username], TopicTable) of {ok, TopicTable1} -> emqttd_session:subscribe(Session, PacketId, TopicTable1), {ok, State}; {stop, _} -> @@ -244,9 +260,11 @@ process(?SUBSCRIBE_PACKET(PacketId, RawTopicTable), State = #proto_state{session process(?UNSUBSCRIBE_PACKET(PacketId, []), State) -> send(?UNSUBACK_PACKET(PacketId), State); -process(?UNSUBSCRIBE_PACKET(PacketId, RawTopics), State = #proto_state{ - client_id = ClientId, username = Username, session = Session}) -> - case emqttd:run_hooks('client.unsubscribe', [ClientId, Username], parse_topics(RawTopics)) of +process(?UNSUBSCRIBE_PACKET(PacketId, RawTopics), + State = #proto_state{client_id = ClientId, + username = Username, + session = Session}) -> + case emqttd_hooks:run('client.unsubscribe', [ClientId, Username], parse_topics(RawTopics)) of {ok, TopicTable} -> emqttd_session:unsubscribe(Session, TopicTable); {stop, _} -> @@ -262,7 +280,9 @@ process(?PACKET(?DISCONNECT), State) -> {stop, normal, State#proto_state{will_msg = undefined}}. publish(Packet = ?PUBLISH_PACKET(?QOS_0, _PacketId), - #proto_state{client_id = ClientId, username = Username, session = Session}) -> + #proto_state{client_id = ClientId, + username = Username, + session = Session}) -> Msg = emqttd_message:from_packet(Username, ClientId, Packet), emqttd_session:publish(Session, Msg); @@ -287,7 +307,7 @@ with_puback(Type, Packet = ?PUBLISH_PACKET(_Qos, PacketId), -spec(send(mqtt_message() | mqtt_packet(), proto_state()) -> {ok, proto_state()}). send(Msg, State = #proto_state{client_id = ClientId, username = Username}) when is_record(Msg, mqtt_message) -> - emqttd:run_hooks('message.delivered', [ClientId, Username], Msg), + emqttd_hooks:run('message.delivered', [ClientId, Username], Msg), send(emqttd_message:to_packet(Msg), State); send(Packet, State = #proto_state{sendfun = SendFun}) @@ -297,15 +317,15 @@ send(Packet, State = #proto_state{sendfun = SendFun}) SendFun(Packet), {ok, State}. -trace(recv, Packet, ProtoState) -> +trace(recv, Packet = ?PACKET(Type), ProtoState) -> + inc(recv_pkt), ?IF(Type =:= ?PUBLISH, inc(recv_msg), ok), ?LOG(info, "RECV ~s", [emqttd_packet:format(Packet)], ProtoState); -trace(send, Packet, ProtoState) -> +trace(send, Packet = ?PACKET(Type), ProtoState) -> + inc(send_pkt), ?IF(Type =:= ?PUBLISH, inc(send_msg), ok), ?LOG(info, "SEND ~s", [emqttd_packet:format(Packet)], ProtoState). -%% @doc redeliver PUBREL PacketId -redeliver({?PUBREL, PacketId}, State) -> - send(?PUBREL_PACKET(PacketId), State). +inc(Key) -> put(Key, get(Key) + 1). stop_if_auth_failure(RC, State) when RC == ?CONNACK_CREDENTIALS; RC == ?CONNACK_AUTH -> {stop, {shutdown, auth_failure}, State}; @@ -325,7 +345,7 @@ shutdown(Error, State = #proto_state{will_msg = WillMsg}) -> ?LOG(info, "Shutdown for ~p", [Error], State), Client = client(State), send_willmsg(Client, WillMsg), - emqttd:run_hooks('client.disconnected', [Error], Client), + emqttd_hooks:run('client.disconnected', [Error], Client), %% let it down %% emqttd_cm:unreg(ClientId). ok. @@ -375,19 +395,19 @@ validate_protocol(#mqtt_packet_connect{proto_ver = Ver, proto_name = Name}) -> validate_clientid(#mqtt_packet_connect{client_id = ClientId}, #proto_state{max_clientid_len = MaxLen}) - when (size(ClientId) >= 1) andalso (size(ClientId) =< MaxLen) -> + when (byte_size(ClientId) >= 1) andalso (byte_size(ClientId) =< MaxLen) -> true; %% Issue#599: Null clientId and clean_sess = false validate_clientid(#mqtt_packet_connect{client_id = ClientId, clean_sess = CleanSess}, _ProtoState) - when size(ClientId) == 0 andalso (not CleanSess) -> + when byte_size(ClientId) == 0 andalso (not CleanSess) -> false; %% MQTT3.1.1 allow null clientId. -validate_clientid(#mqtt_packet_connect{proto_ver =?MQTT_PROTO_V311, +validate_clientid(#mqtt_packet_connect{proto_ver =?MQTT_PROTO_V4, client_id = ClientId}, _ProtoState) - when size(ClientId) =:= 0 -> + when byte_size(ClientId) =:= 0 -> true; validate_clientid(#mqtt_packet_connect{proto_ver = ProtoVer, From d5ac9f0dcbb4e7b4a5da5f6234be1901897cc244 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Thu, 16 Feb 2017 11:33:50 +0800 Subject: [PATCH 033/125] Update protocol_name/1, type_name/1 functions for MQTT 5 --- src/emqttd_packet.erl | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/emqttd_packet.erl b/src/emqttd_packet.erl index 38de31fe0..6349e58b1 100644 --- a/src/emqttd_packet.erl +++ b/src/emqttd_packet.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -14,9 +14,10 @@ %% limitations under the License. %%-------------------------------------------------------------------- -%% @doc MQTT Packet Functions -module(emqttd_packet). +-author("Feng Lee "). + -include("emqttd.hrl"). -include("emqttd_protocol.hrl"). @@ -28,12 +29,13 @@ %% @doc Protocol name of version -spec(protocol_name(mqtt_vsn()) -> binary()). -protocol_name(Ver) when Ver =:= ?MQTT_PROTO_V31; Ver =:= ?MQTT_PROTO_V311 -> - proplists:get_value(Ver, ?PROTOCOL_NAMES). +protocol_name(?MQTT_PROTO_V3) -> <<"MQIsdp">>; +protocol_name(?MQTT_PROTO_V4) -> <<"MQTT">>; +protocol_name(?MQTT_PROTO_V5) -> <<"MQTT">>. %% @doc Name of MQTT packet type -spec(type_name(mqtt_packet_type()) -> atom()). -type_name(Type) when Type > ?RESERVED andalso Type =< ?DISCONNECT -> +type_name(Type) when Type > ?RESERVED andalso Type =< ?AUTH -> lists:nth(Type, ?TYPE_NAMES). %% @doc Connack Name From d91e49a5a5dfb25e6593af6f84e788e8e3d09d99 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Thu, 16 Feb 2017 11:35:33 +0800 Subject: [PATCH 034/125] Change the restart strategy of the top supervisor --- src/emqttd_sup.erl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/emqttd_sup.erl b/src/emqttd_sup.erl index 59ffde2ea..e38d20d65 100644 --- a/src/emqttd_sup.erl +++ b/src/emqttd_sup.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -14,11 +14,12 @@ %% limitations under the License. %%-------------------------------------------------------------------- -%% @doc emqttd top supervisor. -module(emqttd_sup). -behaviour(supervisor). +-author("Feng Lee "). + -include("emqttd.hrl"). %% API @@ -50,5 +51,5 @@ start_child(Mod, Type) when is_atom(Mod) and is_atom(Type) -> %%-------------------------------------------------------------------- init([]) -> - {ok, {{one_for_all, 10, 3600}, []}}. + {ok, {{one_for_all, 0, 1}, []}}. From fa8882b499e35d579a499f410eac2524c691dc3d Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Thu, 16 Feb 2017 11:36:08 +0800 Subject: [PATCH 035/125] Support client, session stats --- src/emqttd_stats.erl | 74 ++++++++++++++++++++++++++++++++------------ 1 file changed, 55 insertions(+), 19 deletions(-) diff --git a/src/emqttd_stats.erl b/src/emqttd_stats.erl index cae57207f..73a1471ac 100644 --- a/src/emqttd_stats.erl +++ b/src/emqttd_stats.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -14,34 +14,39 @@ %% limitations under the License. %%-------------------------------------------------------------------- -%% @doc emqttd statistics -module(emqttd_stats). --include("emqttd.hrl"). - -behaviour(gen_server). --define(SERVER, ?MODULE). +-author("Feng Lee "). + +-include("emqttd.hrl"). -export([start_link/0, stop/0]). -%% statistics API. --export([statsfun/1, statsfun/2, - getstats/0, getstat/1, - setstat/2, setstats/3]). +%% Client and Session Stats +-export([set_client_stats/2, get_client_stats/1, del_client_stats/1, + set_session_stats/2, get_session_stats/1, del_session_stats/1]). + +%% Statistics API. +-export([statsfun/1, statsfun/2, getstats/0, getstat/1, setstat/2, setstats/3]). %% gen_server Function Exports -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). --record(state, {tick_tref}). +-record(state, {tick}). + +-type(stats() :: list({atom(), non_neg_integer()})). -define(STATS_TAB, mqtt_stats). +-define(CLIENT_STATS_TAB, mqtt_client_stats). +-define(SESSION_STATS_TAB, mqtt_session_stats). %% $SYS Topics for Clients -define(SYSTOP_CLIENTS, [ - 'clients/count', % clients connected current - 'clients/max' % max clients connected + 'clients/count', % clients connected current + 'clients/max' % max clients connected ]). %% $SYS Topics for Sessions @@ -75,10 +80,40 @@ %% @doc Start stats server -spec(start_link() -> {ok, pid()} | ignore | {error, term()}). start_link() -> - gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). + gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). stop() -> - gen_server:call(?SERVER, stop). + gen_server:call(?MODULE, stop). + +-spec(set_client_stats(binary(), stats()) -> true). +set_client_stats(ClientId, Stats) -> + ets:insert(?CLIENT_STATS_TAB, {ClientId, [{'$ts', emqttd_time:now_secs()}|Stats]}). + +-spec(get_client_stats(binary()) -> stats()). +get_client_stats(ClientId) -> + case ets:lookup(?CLIENT_STATS_TAB, ClientId) of + [{_, Stats}] -> Stats; + [] -> [] + end. + +-spec(del_client_stats(binary()) -> true). +del_client_stats(ClientId) -> + ets:delete(?CLIENT_STATS_TAB, ClientId). + +-spec(set_session_stats(binary(), stats()) -> true). +set_session_stats(ClientId, Stats) -> + ets:insert(?SESSION_STATS_TAB, {ClientId, [{'$ts', emqttd_time:now_secs()}|Stats]}). + +-spec(get_session_stats(binary()) -> stats()). +get_session_stats(ClientId) -> + case ets:lookup(?SESSION_STATS_TAB, ClientId) of + [{_, Stats}] -> Stats; + [] -> [] + end. + +-spec(del_session_stats(binary()) -> true). +del_session_stats(ClientId) -> + ets:delete(?SESSION_STATS_TAB, ClientId). %% @doc Generate stats fun -spec(statsfun(Stat :: atom()) -> fun()). @@ -118,13 +153,14 @@ setstats(Stat, MaxStat, Val) -> init([]) -> emqttd_time:seed(), - ets:new(?STATS_TAB, [set, public, named_table, {write_concurrency, true}]), + lists:foreach( + fun(Tab) -> + Tab = ets:new(Tab, [set, public, named_table, {write_concurrency, true}]) + end, [?STATS_TAB, ?CLIENT_STATS_TAB, ?SESSION_STATS_TAB]), Topics = ?SYSTOP_CLIENTS ++ ?SYSTOP_SESSIONS ++ ?SYSTOP_PUBSUB ++ ?SYSTOP_RETAINED, ets:insert(?STATS_TAB, [{Topic, 0} || Topic <- Topics]), - % Create $SYS Topics - % [ok = emqttd:create(topic, stats_topic(Topic)) || Topic <- Topics], % Tick to publish stats - {ok, #state{tick_tref = emqttd_broker:start_tick(tick)}, hibernate}. + {ok, #state{tick = emqttd_broker:start_tick(tick)}, hibernate}. handle_call(stop, _From, State) -> {stop, normal, ok, State}; @@ -154,7 +190,7 @@ handle_info(tick, State) -> handle_info(_Info, State) -> {noreply, State}. -terminate(_Reason, #state{tick_tref = TRef}) -> +terminate(_Reason, #state{tick = TRef}) -> emqttd_broker:stop_tick(TRef). code_change(_OldVsn, State, _Extra) -> From 78c8856eda11070fb5136575513cc2d8b6c7d562 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Thu, 16 Feb 2017 11:36:24 +0800 Subject: [PATCH 036/125] Update copyright info --- src/emqttd_sm_helper.erl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/emqttd_sm_helper.erl b/src/emqttd_sm_helper.erl index 97b356c8d..0f7cb7ef1 100644 --- a/src/emqttd_sm_helper.erl +++ b/src/emqttd_sm_helper.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -17,6 +17,8 @@ %% @doc Session Helper. -module(emqttd_sm_helper). +-author("Feng Lee "). + -behaviour(gen_server). -include("emqttd.hrl"). From d69d7697979d813594640f793e0d39a1ff5f6362 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Thu, 16 Feb 2017 11:36:57 +0800 Subject: [PATCH 037/125] Upgrade copyright info --- src/emqttd_session_sup.erl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/emqttd_session_sup.erl b/src/emqttd_session_sup.erl index 88766b11a..bd9b34f02 100644 --- a/src/emqttd_session_sup.erl +++ b/src/emqttd_session_sup.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -14,9 +14,11 @@ %% limitations under the License. %%-------------------------------------------------------------------- -%% @doc emqttd session supervisor. +%% @doc Session Supervisor. -module(emqttd_session_sup). +-author("Feng Lee "). + -behavior(supervisor). -export([start_link/0, start_session/3]). @@ -41,4 +43,3 @@ init([]) -> {ok, {{simple_one_for_one, 0, 1}, [{session, {emqttd_session, start_link, []}, temporary, 5000, worker, [emqttd_session]}]}}. - From 45a379f11299dc87246ba17261e9c21101dc7e4d Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Thu, 16 Feb 2017 11:38:40 +0800 Subject: [PATCH 038/125] Replace 'size/1' with 'byte_size/1', serialize to output iolist --- src/emqttd_serializer.erl | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/emqttd_serializer.erl b/src/emqttd_serializer.erl index f08ed7eef..ed920f584 100644 --- a/src/emqttd_serializer.erl +++ b/src/emqttd_serializer.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -17,6 +17,8 @@ %% @doc MQTT Packet Serializer -module(emqttd_serializer). +-author("Feng Lee "). + -include("emqttd.hrl"). -include("emqttd_protocol.hrl"). @@ -25,7 +27,7 @@ -export([serialize/1]). %% @doc Serialise MQTT Packet --spec(serialize(mqtt_packet()) -> binary()). +-spec(serialize(mqtt_packet()) -> iolist()). serialize(#mqtt_packet{header = Header = #mqtt_packet_header{type = Type}, variable = Variable, payload = Payload}) -> @@ -37,14 +39,12 @@ serialize_header(#mqtt_packet_header{type = Type, dup = Dup, qos = Qos, retain = Retain}, - {VariableBin, PayloadBin}) when ?CONNECT =< Type andalso Type =< ?DISCONNECT -> - Len = size(VariableBin) + size(PayloadBin), + {VariableBin, PayloadBin}) + when ?CONNECT =< Type andalso Type =< ?DISCONNECT -> + Len = byte_size(VariableBin) + byte_size(PayloadBin), true = (Len =< ?MAX_LEN), - LenBin = serialize_len(Len), - <>. + [<>, + serialize_len(Len), VariableBin, PayloadBin]. serialize_variable(?CONNECT, #mqtt_packet_connect{client_id = ClientId, proto_ver = ProtoVer, @@ -58,7 +58,7 @@ serialize_variable(?CONNECT, #mqtt_packet_connect{client_id = ClientId, will_msg = WillMsg, username = Username, password = Password}, undefined) -> - VariableBin = <<(size(ProtoName)):16/big-unsigned-integer, + VariableBin = <<(byte_size(ProtoName)):16/big-unsigned-integer, ProtoName/binary, ProtoVer:8, (opt(Username)):1, @@ -73,7 +73,7 @@ serialize_variable(?CONNECT, #mqtt_packet_connect{client_id = ClientId, PayloadBin1 = case WillFlag of true -> <>; false -> PayloadBin end, @@ -93,7 +93,7 @@ serialize_variable(?SUBACK, #mqtt_packet_suback{packet_id = PacketId, {<>, << <> || Q <- QosTable >>}; serialize_variable(?UNSUBSCRIBE, #mqtt_packet_unsubscribe{packet_id = PacketId, - topics = Topics }, undefined) -> + topics = Topics }, undefined) -> {<>, serialize_topics(Topics)}; serialize_variable(?UNSUBACK, #mqtt_packet_unsuback{packet_id = PacketId}, undefined) -> @@ -134,7 +134,7 @@ serialize_topics([H|_] = Topics) when is_binary(H) -> serialize_utf(String) -> StringBin = unicode:characters_to_binary(String), - Len = size(StringBin), + Len = byte_size(StringBin), true = (Len =< 16#ffff), <>. @@ -148,4 +148,3 @@ opt(false) -> 0; opt(true) -> 1; opt(X) when is_integer(X) -> X; opt(B) when is_binary(B) -> 1. - From a345b3682b645f1292f48c2ba648ce171baa16e6 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Thu, 16 Feb 2017 11:39:48 +0800 Subject: [PATCH 039/125] Rename emqttd_hook to emqttd_hooks --- src/emqttd_server.erl | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/emqttd_server.erl b/src/emqttd_server.erl index f21bd5d4b..ec57f2802 100644 --- a/src/emqttd_server.erl +++ b/src/emqttd_server.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -16,10 +16,10 @@ -module(emqttd_server). --author("Feng Lee "). - -behaviour(gen_server2). +-author("Feng Lee "). + -include("emqttd.hrl"). -include("emqttd_protocol.hrl"). @@ -43,7 +43,7 @@ %% Debug API -export([dump/0]). -%% gen_server. +%% gen_server Callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). @@ -89,7 +89,7 @@ async_subscribe(Topic, Subscriber, Options) when is_binary(Topic) -> -spec(publish(mqtt_message()) -> {ok, mqtt_delivery()} | ignore). publish(Msg = #mqtt_message{from = From}) -> trace(publish, From, Msg), - case emqttd_hook:run('message.publish', [], Msg) of + case emqttd_hooks:run('message.publish', [], Msg) of {ok, Msg1 = #mqtt_message{topic = Topic}} -> emqttd_pubsub:publish(Topic, Msg1); {stop, Msg1} -> @@ -97,14 +97,13 @@ publish(Msg = #mqtt_message{from = From}) -> ignore end. +%% @private trace(publish, From, _Msg) when is_atom(From) -> %% Dont' trace '$SYS' publish ignore; - trace(publish, {ClientId, Username}, #mqtt_message{topic = Topic, payload = Payload}) -> lager:info([{client, ClientId}, {topic, Topic}], "~s/~s PUBLISH to ~s: ~p", [ClientId, Username, Topic, Payload]); - trace(publish, From, #mqtt_message{topic = Topic, payload = Payload}) when is_binary(From); is_list(From) -> lager:info([{client, From}, {topic, Topic}], "~s PUBLISH to ~s: ~p", [From, Topic, Payload]). @@ -142,7 +141,8 @@ subscriptions(Subscriber) -> subscription(Topic, Subscriber) -> {Topic, Subscriber, ets:lookup_element(mqtt_subproperty, {Topic, Subscriber}, 2)}. -subscribers(Topic) -> emqttd_pubsub:subscribers(Topic). +subscribers(Topic) -> + emqttd_pubsub:subscribers(Topic). -spec(is_subscribed(binary(), emqttd:subscriber()) -> boolean()). is_subscribed(Topic, Subscriber) when is_binary(Topic) -> From bad855bdd93b2e2547947bce25ef5442fb5a9e62 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Thu, 16 Feb 2017 11:46:06 +0800 Subject: [PATCH 040/125] Improve the session design, support tune_qos, enable_stats --- src/emqttd_session.erl | 1011 ++++++++++++++++++++++------------------ 1 file changed, 561 insertions(+), 450 deletions(-) diff --git a/src/emqttd_session.erl b/src/emqttd_session.erl index a73503479..31efaab55 100644 --- a/src/emqttd_session.erl +++ b/src/emqttd_session.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -14,47 +14,60 @@ %% limitations under the License. %%-------------------------------------------------------------------- -%% @doc Session for persistent MQTT client. %% -%% Session State in the broker consists of: +%% @doc MQTT Session %% -%% 1. The Client’s subscriptions. +%% A stateful interaction between a Client and a Server. Some Sessions +%% last only as long as the Network Connection, others can span multiple +%% consecutive Network Connections between a Client and a Server. %% -%% 2. inflight qos1/2 messages sent to the client but unacked, QoS 1 and QoS 2 -%% messages which have been sent to the Client, but have not been completely -%% acknowledged. +%% The Session state in the Server consists of: %% -%% 3. inflight qos2 messages received from client and waiting for pubrel. QoS 2 -%% messages which have been received from the Client, but have not been -%% completely acknowledged. +%% The existence of a Session, even if the rest of the Session state is empty. %% -%% 4. all qos1, qos2 messages published to when client is disconnected. -%% QoS 1 and QoS 2 messages pending transmission to the Client. +%% The Client’s subscriptions. %% -%% 5. Optionally, QoS 0 messages pending transmission to the Client. +%% QoS 1 and QoS 2 messages which have been sent to the Client, but have not +%% been completely acknowledged. %% -%% State of Message: newcome, inflight, pending +%% QoS 1 and QoS 2 messages pending transmission to the Client. +%% +%% QoS 2 messages which have been received from the Client, but have not +%% been completely acknowledged. +%% +%% Optionally, QoS 0 messages pending transmission to the Client. +%% +%% If the session is currently disconnected, the time at which the Session state +%% will be deleted. %% %% @end +%% -module(emqttd_session). +-behaviour(gen_server2). + +-author("Feng Lee "). + -include("emqttd.hrl"). -include("emqttd_protocol.hrl"). -include("emqttd_internal.hrl"). --behaviour(gen_server2). +-import(emqttd_misc, [start_timer/2]). -import(proplists, [get_value/2, get_value/3]). %% Session API --export([start_link/3, resume/3, info/1, destroy/2]). +-export([start_link/3, resume/3, destroy/2]). -%% PubSub APIs --export([publish/2, puback/2, pubrec/2, pubrel/2, pubcomp/2, - subscribe/2, subscribe/3, unsubscribe/2]). +%% Management and Monitor API +-export([state/1, info/1, stats/1]). + +%% PubSub API +-export([subscribe/2, subscribe/3, publish/2, puback/2, pubrec/2, + pubrel/2, pubcomp/2, unsubscribe/2]). %% gen_server Function Exports -export([init/1, handle_call/3, handle_cast/2, handle_info/2, @@ -63,150 +76,199 @@ %% gen_server2 Message Priorities -export([prioritise_call/4, prioritise_cast/3, prioritise_info/3]). --record(session, { +-record(state, + { + %% Clean Session Flag + clean_sess = false :: boolean(), - %% Clean Session Flag - clean_sess = true, + %% Client Binding: local | remote + binding = local :: local | remote, - %% ClientId: Identifier of Session - client_id :: binary(), + %% ClientId: Identifier of Session + client_id :: binary(), - %% Client Pid bind with session - client_pid :: pid(), + %% Username + username :: binary() | undefined, - %% Old Client Pid that has been kickout - old_client_pid :: pid(), + %% Client Pid binding with session + client_pid :: pid(), - %% Username - username :: binary() | undefined, + %% Old Client Pid that has been kickout + old_client_pid :: pid(), - %% Last packet id of the session - packet_id = 1, - - %% Client’s subscriptions. - subscriptions :: map(), + %% Next message id of the session + next_msg_id = 1 :: mqtt_packet_id(), - %% Inflight qos1, qos2 messages sent to the client but unacked, - %% QoS 1 and QoS 2 messages which have been sent to the Client, - %% but have not been completely acknowledged. - %% Client <- Broker - inflight_queue :: list(), + max_subscriptions :: non_neg_integer(), - max_inflight = 0, + %% Client’s subscriptions. + subscriptions :: map(), - %% All qos1, qos2 messages published to when client is disconnected. - %% QoS 1 and QoS 2 messages pending transmission to the Client. - %% - %% Optionally, QoS 0 messages pending transmission to the Client. - message_queue :: emqttd_mqueue:mqueue(), + %% Upgrade Qos? + upgrade_qos = false :: boolean(), - %% Inflight qos2 messages received from client and waiting for pubrel. - %% QoS 2 messages which have been received from the Client, - %% but have not been completely acknowledged. - %% Client -> Broker - awaiting_rel :: map(), + %% Client <- Broker: Inflight QoS1, QoS2 messages sent to the client but unacked. + inflight :: emqttd_inflight:inflight(), - %% Awaiting PUBREL timeout - await_rel_timeout = 8, + %% Max Inflight Size + max_inflight = 32 :: non_neg_integer(), - %% Max Packets that Awaiting PUBREL - max_awaiting_rel = 100, + %% Retry interval for redelivering QoS1/2 messages + retry_interval = 20000 :: pos_integer(), - %% Awaiting timers for ack, rel. - awaiting_ack :: map(), + %% Retry Timer + retry_timer :: reference(), - %% Retry interval for redelivering QoS1/2 messages - retry_interval = 20, + %% All QoS1, QoS2 messages published to when client is disconnected. + %% QoS 1 and QoS 2 messages pending transmission to the Client. + %% + %% Optionally, QoS 0 messages pending transmission to the Client. + mqueue :: emqttd_mqueue:mqueue(), - %% Awaiting for PUBCOMP - awaiting_comp :: map(), + %% Client -> Broker: Inflight QoS2 messages received from client and waiting for pubrel. + awaiting_rel :: map(), - %% session expired after 48 hours - expired_after = 172800, + %% Awaiting PUBREL timeout + await_rel_timeout = 20000 :: pos_integer(), - expired_timer, + %% Max Packets that Awaiting PUBREL + max_awaiting_rel = 100 :: non_neg_integer(), - collect_interval, + %% Awaiting PUBREL timer + await_rel_timer :: reference(), - collect_timer, - - timestamp}). + %% Session Expiry Interval + expiry_interval = 7200000 :: pos_integer(), --define(PUBSUB_TIMEOUT, 60000). + %% Expired Timer + expiry_timer :: reference(), + + %% Enable Stats + enable_stats :: false | pos_integer(), + + %% Stats Timer + stats_timer :: reference(), + + created_at :: erlang:timestamp() + }). + +-define(TIMEOUT, 60000). + +-define(INFO_KEYS, [clean_sess, client_id, username, client_pid, binding, created_at]). + +-define(STATE_KEYS, [clean_sess, client_id, username, binding, client_pid, old_client_pid, + next_msg_id, max_subscriptions, subscriptions, upgrade_qos, inflight, + max_inflight, retry_interval, mqueue, awaiting_rel, max_awaiting_rel, + await_rel_timeout, expiry_interval, enable_stats, created_at]). -define(LOG(Level, Format, Args, State), - lager:Level([{client, State#session.client_id}], - "Session(~s): " ++ Format, [State#session.client_id | Args])). + lager:Level([{client, State#state.client_id}], + "Session(~s): " ++ Format, [State#state.client_id | Args])). -%% @doc Start a session. +%% @doc Start a Session -spec(start_link(boolean(), {mqtt_client_id(), mqtt_username()}, pid()) -> {ok, pid()} | {error, any()}). start_link(CleanSess, {ClientId, Username}, ClientPid) -> gen_server2:start_link(?MODULE, [CleanSess, {ClientId, Username}, ClientPid], []). -%% @doc Resume a session. --spec(resume(pid(), mqtt_client_id(), pid()) -> ok). -resume(SessPid, ClientId, ClientPid) -> - gen_server2:cast(SessPid, {resume, ClientId, ClientPid}). - -%% @doc Session Info. -info(SessPid) -> - gen_server2:call(SessPid, info). - -%% @doc Destroy a session. --spec(destroy(pid(), mqtt_client_id()) -> ok). -destroy(SessPid, ClientId) -> - gen_server2:cast(SessPid, {destroy, ClientId}). - %%-------------------------------------------------------------------- -%% PubSub +%% PubSub API %%-------------------------------------------------------------------- -%% @doc Subscribe Topics +%% @doc Subscribe topics -spec(subscribe(pid(), [{binary(), [emqttd_topic:option()]}]) -> ok). -subscribe(SessPid, TopicTable) -> - gen_server2:cast(SessPid, {subscribe, TopicTable, fun(_) -> ok end}). +subscribe(Session, TopicTable) ->%%TODO: the ack function??... + gen_server2:cast(Session, {subscribe, self(), TopicTable, fun(_) -> ok end}). --spec(subscribe(pid(), mqtt_pktid(), [{binary(), [emqttd_topic:option()]}]) -> ok). -subscribe(SessPid, PktId, TopicTable) -> +-spec(subscribe(pid(), mqtt_packet_id(), [{binary(), [emqttd_topic:option()]}]) -> ok). +subscribe(Session, PacketId, TopicTable) -> %%TODO: the ack function??... From = self(), - AckFun = fun(GrantedQos) -> From ! {suback, PktId, GrantedQos} end, - gen_server2:cast(SessPid, {subscribe, TopicTable, AckFun}). + AckFun = fun(GrantedQos) -> From ! {suback, PacketId, GrantedQos} end, + gen_server2:cast(Session, {subscribe, From, TopicTable, AckFun}). -%% @doc Publish message +%% @doc Publish Message -spec(publish(pid(), mqtt_message()) -> ok | {error, any()}). -publish(_SessPid, Msg = #mqtt_message{qos = ?QOS_0}) -> - %% publish qos0 directly - emqttd:publish(Msg), ok; +publish(_Session, Msg = #mqtt_message{qos = ?QOS_0}) -> + %% Publish QoS0 Directly + emqttd_server:publish(Msg), ok; -publish(_SessPid, Msg = #mqtt_message{qos = ?QOS_1}) -> - %% publish qos1 directly, and client will puback automatically - emqttd:publish(Msg), ok; +publish(_Session, Msg = #mqtt_message{qos = ?QOS_1}) -> + %% Publish QoS1 message directly for client will PubAck automatically + emqttd_server:publish(Msg), ok; -publish(SessPid, Msg = #mqtt_message{qos = ?QOS_2}) -> - %% publish qos2 by session - gen_server2:call(SessPid, {publish, Msg}, ?PUBSUB_TIMEOUT). +publish(Session, Msg = #mqtt_message{qos = ?QOS_2}) -> + %% Publish QoS2 to Session + gen_server2:call(Session, {publish, Msg}, ?TIMEOUT). -%% @doc PubAck message +%% @doc PubAck Message -spec(puback(pid(), mqtt_packet_id()) -> ok). -puback(SessPid, PktId) -> - gen_server2:cast(SessPid, {puback, PktId}). +puback(Session, PacketId) -> + gen_server2:cast(Session, {puback, PacketId}). -spec(pubrec(pid(), mqtt_packet_id()) -> ok). -pubrec(SessPid, PktId) -> - gen_server2:cast(SessPid, {pubrec, PktId}). +pubrec(Session, PacketId) -> + gen_server2:cast(Session, {pubrec, PacketId}). -spec(pubrel(pid(), mqtt_packet_id()) -> ok). -pubrel(SessPid, PktId) -> - gen_server2:cast(SessPid, {pubrel, PktId}). +pubrel(Session, PacketId) -> + gen_server2:cast(Session, {pubrel, PacketId}). -spec(pubcomp(pid(), mqtt_packet_id()) -> ok). -pubcomp(SessPid, PktId) -> - gen_server2:cast(SessPid, {pubcomp, PktId}). +pubcomp(Session, PacketId) -> + gen_server2:cast(Session, {pubcomp, PacketId}). -%% @doc Unsubscribe Topics +%% @doc Unsubscribe the topics -spec(unsubscribe(pid(), [{binary(), [emqttd_topic:option()]}]) -> ok). -unsubscribe(SessPid, TopicTable) -> - gen_server2:cast(SessPid, {unsubscribe, TopicTable}). +unsubscribe(Session, TopicTable) -> + gen_server2:cast(Session, {unsubscribe, self(), TopicTable}). + +%% @doc Resume the session +-spec(resume(pid(), mqtt_client_id(), pid()) -> ok). +resume(Session, ClientId, ClientPid) -> + gen_server2:cast(Session, {resume, ClientId, ClientPid}). + +%% @doc Get session state +state(Session) when is_pid(Session) -> + gen_server2:call(Session, state). + +%% @doc Get session info +-spec(info(pid() | #state{}) -> list(tuple())). +info(Session) when is_pid(Session) -> + gen_server2:call(Session, info); + +info(State) when is_record(State, state) -> + ?record_to_proplist(state, State, ?INFO_KEYS). + +-spec(stats(pid() | #state{}) -> list({atom(), non_neg_integer()})). +stats(Session) when is_pid(Session) -> + gen_server2:call(Session, stats); + +stats(#state{max_subscriptions = MaxSubscriptions, + subscriptions = Subscriptions, + inflight = Inflight, + max_inflight = MaxInflight, + mqueue = MQueue, + max_awaiting_rel = MaxAwaitingRel, + awaiting_rel = AwaitingRel}) -> + lists:append(emqttd_misc:proc_stats(), + [{max_subscriptions, MaxSubscriptions}, + {subscriptions, maps:size(Subscriptions)}, + {max_inflight, MaxInflight}, + {inflight_len, Inflight:size()}, + {max_mqueue, case emqttd_mqueue:max_len(MQueue) of + infinity -> 0; + Len -> Len + end}, + {mqueue_len, emqttd_mqueue:len(MQueue)}, + {mqueue_dropped, emqttd_mqueue:dropped(MQueue)}, + {max_awaiting_rel, MaxAwaitingRel}, + {awaiting_rel_len, maps:size(AwaitingRel)}, + {deliver_msg, get(deliver_msg)}, + {enqueue_msg, get(enqueue_msg)}]). + +%% @doc Destroy the session +-spec(destroy(pid(), mqtt_client_id()) -> ok). +destroy(Session, ClientId) -> + gen_server2:cast(Session, {destroy, ClientId}). %%-------------------------------------------------------------------- %% gen_server Callbacks @@ -215,36 +277,43 @@ unsubscribe(SessPid, TopicTable) -> init([CleanSess, {ClientId, Username}, ClientPid]) -> process_flag(trap_exit, true), true = link(ClientPid), + init_stats([deliver_msg, enqueue_msg]), + {ok, Env} = emqttd:env(session), {ok, QEnv} = emqttd:env(queue), - {ok, SessEnv} = emqttd:env(session), - Session = #session{ - clean_sess = CleanSess, - client_id = ClientId, - client_pid = ClientPid, - username = Username, - subscriptions = #{}, - inflight_queue = [], - max_inflight = get_value(max_inflight, SessEnv, 0), - message_queue = emqttd_mqueue:new(ClientId, QEnv, emqttd_alarm:alarm_fun()), - awaiting_rel = #{}, - awaiting_ack = #{}, - awaiting_comp = #{}, - retry_interval = get_value(retry_interval, SessEnv), - await_rel_timeout = get_value(await_rel_timeout, SessEnv), - max_awaiting_rel = get_value(max_awaiting_rel, SessEnv), - expired_after = get_value(expired_after, SessEnv), - collect_interval = get_value(collect_interval, SessEnv, 0), - timestamp = os:timestamp()}, - emqttd_sm:reg_session(ClientId, CleanSess, sess_info(Session)), - emqttd:run_hooks('session.created', [ClientId, Username]), - %% Start statistics - {ok, start_collector(Session), hibernate}. + MaxInflight = get_value(max_inflight, Env, 0), + EnableStats = get_value(enable_stats, Env, false), + MQueue = emqttd_mqueue:new(ClientId, QEnv, emqttd_alarm:alarm_fun()), + State = #state{clean_sess = CleanSess, + binding = binding(ClientPid), + client_id = ClientId, + client_pid = ClientPid, + username = Username, + subscriptions = #{}, + max_subscriptions = get_value(max_subscriptions, Env, 0), + upgrade_qos = get_value(upgrade_qos, Env, false), + max_inflight = MaxInflight, + inflight = emqttd_inflight:new(MaxInflight), + mqueue = MQueue, + retry_interval = get_value(retry_interval, Env), + awaiting_rel = #{}, + await_rel_timeout = get_value(await_rel_timeout, Env), + max_awaiting_rel = get_value(max_awaiting_rel, Env), + expiry_interval = get_value(expiry_interval, Env), + enable_stats = EnableStats, + created_at = os:timestamp()}, + emqttd_stats:set_session_stats(ClientId, stats(State)), + emqttd_sm:register_session(ClientId, CleanSess, info(State)), + emqttd_hooks:run('session.created', [ClientId, Username]), + {ok, State, hibernate, {backoff, 1000, 1000, 5000}, ?MODULE}. + +init_stats(Keys) -> + lists:foreach(fun(K) -> put(K, 0) end, Keys). + +binding(ClientPid) -> + case node(ClientPid) =:= node() of true -> local; false -> remote end. prioritise_call(Msg, _From, _Len, _State) -> - case Msg of - info -> 10; - _ -> 0 - end. + case Msg of info -> 10; stats -> 10; state -> 10; _ -> 5 end. prioritise_cast(Msg, _Len, _State) -> case Msg of @@ -262,279 +331,249 @@ prioritise_cast(Msg, _Len, _State) -> prioritise_info(Msg, _Len, _State) -> case Msg of {'EXIT', _, _} -> 10; - expired -> 10; {timeout, _, _} -> 5; - collect_info -> 2; {dispatch, _, _} -> 1; _ -> 0 end. -handle_call(info, _From, State) -> - {reply, sess_info(State), State, hibernate}; - -handle_call({publish, Msg = #mqtt_message{qos = ?QOS_2, pktid = PktId}}, - _From, Session = #session{awaiting_rel = AwaitingRel, - await_rel_timeout = Timeout}) -> - case check_awaiting_rel(Session) of - true -> - TRef = timer(Timeout, {timeout, awaiting_rel, PktId}), - AwaitingRel1 = maps:put(PktId, {Msg, TRef}, AwaitingRel), - {reply, ok, Session#session{awaiting_rel = AwaitingRel1}}; +handle_call({publish, Msg = #mqtt_message{qos = ?QOS_2, pktid = PacketId}}, + _From, State = #state{awaiting_rel = AwaitingRel, + await_rel_timer = Timer, + await_rel_timeout = Timeout}) -> + case is_awaiting_full(State) of false -> - ?LOG(critical, "Dropped Qos2 message for too many awaiting_rel: ~p", [Msg], Session), - {reply, {error, dropped}, Session, hibernate} + State1 = case Timer == undefined of + true -> State#state{await_rel_timer = start_timer(Timeout, check_awaiting_rel)}; + false -> State + end, + reply(ok, State1#state{awaiting_rel = maps:put(PacketId, Msg, AwaitingRel)}); + true -> + ?LOG(warning, "Dropped Qos2 Message for too many awaiting_rel: ~p", [Msg], State), + emqttd_metrics:inc('messages/qos2/dropped'), + reply({error, dropped}, State) end; +handle_call(info, _From, State) -> + reply(info(State), State); + +handle_call(stats, _From, State) -> + reply(stats(State), State); + +handle_call(state, _From, State) -> + reply(?record_to_proplist(state, State, ?STATE_KEYS), State); + handle_call(Req, _From, State) -> ?UNEXPECTED_REQ(Req, State). -handle_cast({subscribe, TopicTable, AckFun}, Session = #session{client_id = ClientId, - username = Username, - subscriptions = Subscriptions}) -> - ?LOG(info, "Subscribe ~p", [TopicTable], Session), +handle_cast({subscribe, _From, TopicTable, AckFun}, + State = #state{client_id = ClientId, + username = Username, + subscriptions = Subscriptions}) -> + ?LOG(info, "Subscribe ~p", [TopicTable], State), {GrantedQos, Subscriptions1} = lists:foldl(fun({Topic, Opts}, {QosAcc, SubMap}) -> NewQos = proplists:get_value(qos, Opts), SubMap1 = case maps:find(Topic, SubMap) of {ok, NewQos} -> - ?LOG(warning, "duplicated subscribe: ~s, qos = ~w", [Topic, NewQos], Session), + ?LOG(warning, "Duplicated subscribe: ~s, qos = ~w", [Topic, NewQos], State), SubMap; {ok, OldQos} -> emqttd:setqos(Topic, ClientId, NewQos), - ?LOG(warning, "duplicated subscribe ~s, old_qos=~w, new_qos=~w", - [Topic, OldQos, NewQos], Session), + ?LOG(warning, "Duplicated subscribe ~s, old_qos=~w, new_qos=~w", + [Topic, OldQos, NewQos], State), maps:put(Topic, NewQos, SubMap); error -> emqttd:subscribe(Topic, ClientId, Opts), - emqttd:run_hooks('session.subscribed', [ClientId, Username], {Topic, Opts}), + emqttd_hooks:run('session.subscribed', [ClientId, Username], {Topic, Opts}), maps:put(Topic, NewQos, SubMap) end, {[NewQos|QosAcc], SubMap1} end, {[], Subscriptions}, TopicTable), AckFun(lists:reverse(GrantedQos)), - hibernate(Session#session{subscriptions = Subscriptions1}); + noreply(emit_stats(State#state{subscriptions = Subscriptions1})); -handle_cast({unsubscribe, TopicTable}, Session = #session{client_id = ClientId, - username = Username, - subscriptions = Subscriptions}) -> - ?LOG(info, "unsubscribe ~p", [TopicTable], Session), +handle_cast({unsubscribe, _From, TopicTable}, + State = #state{client_id = ClientId, + username = Username, + subscriptions = Subscriptions}) -> + ?LOG(info, "Unsubscribe ~p", [TopicTable], State), Subscriptions1 = lists:foldl(fun({Topic, Opts}, SubMap) -> case maps:find(Topic, SubMap) of {ok, _Qos} -> emqttd:unsubscribe(Topic, ClientId), - emqttd:run_hooks('session.unsubscribed', [ClientId, Username], {Topic, Opts}), + emqttd_hooks:run('session.unsubscribed', [ClientId, Username], {Topic, Opts}), maps:remove(Topic, SubMap); error -> SubMap end end, Subscriptions, TopicTable), - hibernate(Session#session{subscriptions = Subscriptions1}); + noreply(emit_stats(State#state{subscriptions = Subscriptions1})); -handle_cast({destroy, ClientId}, Session = #session{client_id = ClientId, client_pid = undefined}) -> - ?LOG(warning, "destroyed", [], Session), - shutdown(destroy, Session); +%% PUBACK: +handle_cast({puback, PacketId}, State = #state{inflight = Inflight}) -> + case Inflight:contain(PacketId) of + true -> + noreply(dequeue(acked(puback, PacketId, State))); + false -> + ?LOG(warning, "The PUBACK ~p is not inflight: ~p", + [PacketId, Inflight:window()], State), + emqttd_metrics:inc('packets/puback/missed'), + noreply(State) + end; -handle_cast({destroy, ClientId}, Session = #session{client_id = ClientId, client_pid = OldClientPid}) -> - ?LOG(warning, "kickout ~p", [OldClientPid], Session), - shutdown(conflict, Session); +%% PUBREC: +handle_cast({pubrec, PacketId}, State = #state{inflight = Inflight}) -> + case Inflight:contain(PacketId) of + true -> + noreply(acked(pubrec, PacketId, State)); + false -> + ?LOG(warning, "The PUBREC ~p is not inflight: ~p", + [PacketId, Inflight:window()], State), + emqttd_metrics:inc('packets/pubrec/missed'), + noreply(State) + end; -handle_cast({resume, ClientId, ClientPid}, Session = #session{client_id = ClientId, - client_pid = OldClientPid, - clean_sess = CleanSess, - inflight_queue = InflightQ, - awaiting_ack = AwaitingAck, - awaiting_comp = AwaitingComp, - expired_timer = ETimer} = Session) -> +%% PUBREL: +handle_cast({pubrel, PacketId}, State = #state{awaiting_rel = AwaitingRel}) -> + case maps:take(PacketId, AwaitingRel) of + {Msg, AwaitingRel1} -> + spawn(emqttd_server, publish, [Msg]),%%:) + noreply(State#state{awaiting_rel = AwaitingRel1}); + error -> + ?LOG(warning, "Cannot find PUBREL: ~p", [PacketId], State), + emqttd_metrics:inc('packets/pubrel/missed'), + noreply(State) + end; - ?LOG(info, "resumed by ~p", [ClientPid], Session), +%% PUBCOMP: +handle_cast({pubcomp, PacketId}, State = #state{inflight = Inflight}) -> + case Inflight:contain(PacketId) of + true -> + noreply(dequeue(acked(pubcomp, PacketId, State))); + false -> + ?LOG(warning, "The PUBCOMP ~p is not inflight: ~p", + [PacketId, Inflight:window()], State), + emqttd_metrics:inc('packets/pubcomp/missed'), + noreply(State) + end; - %% Cancel expired timer - cancel_timer(ETimer), +%% RESUME: +handle_cast({resume, ClientId, ClientPid}, + State = #state{client_id = ClientId, + client_pid = OldClientPid, + clean_sess = CleanSess, + retry_timer = RetryTimer, + await_rel_timer = AwaitTimer, + stats_timer = StatsTimer, + expiry_timer = ExpireTimer}) -> + + ?LOG(info, "Resumed by ~p", [ClientPid], State), + + %% Cancel Timers + lists:foreach(fun emqttd_misc:cancel_timer/1, + [RetryTimer, AwaitTimer, StatsTimer, ExpireTimer]), case kick(ClientId, OldClientPid, ClientPid) of - ok -> ?LOG(warning, "~p kickout ~p", [ClientPid, OldClientPid], Session); + ok -> ?LOG(warning, "~p kickout ~p", [ClientPid, OldClientPid], State); ignore -> ok end, true = link(ClientPid), - %% Redeliver PUBREL - [ClientPid ! {redeliver, {?PUBREL, PktId}} || PktId <- maps:keys(AwaitingComp)], + State1 = State#state{client_pid = ClientPid, + binding = binding(ClientPid), + old_client_pid = OldClientPid, + clean_sess = false, + retry_timer = undefined, + awaiting_rel = #{}, + await_rel_timer = undefined, + expiry_timer = undefined}, - %% Clear awaiting_ack timers - [cancel_timer(TRef) || TRef <- maps:values(AwaitingAck)], - - %% Clear awaiting_comp timers - [cancel_timer(TRef) || TRef <- maps:values(AwaitingComp)], - - Session1 = Session#session{client_pid = ClientPid, - old_client_pid = OldClientPid, - clean_sess = false, - awaiting_ack = #{}, - awaiting_comp = #{}, - expired_timer = undefined}, - - %% CleanSess: true -> false? + %% Clean Session: true -> false? if - CleanSess =:= true -> - ?LOG(warning, "CleanSess changed to false.", [], Session), - emqttd_sm:reg_session(ClientId, false, sess_info(Session1)); + CleanSess =:= true -> + ?LOG(error, "CleanSess changed to false.", [], State1), + emqttd_sm:register_session(ClientId, false, info(State1)); CleanSess =:= false -> ok end, - %% Redeliver inflight messages - Session2 = - lists:foldl(fun({_Id, Msg}, Sess) -> - redeliver(Msg, Sess) - end, Session1, lists:reverse(InflightQ)), + %% Replay delivery and Dequeue pending messages + noreply(emit_stats(dequeue(retry_delivery(true, State1)))); - %% Dequeue pending messages - hibernate(dequeue(Session2)); +handle_cast({destroy, ClientId}, State = #state{client_id = ClientId, + client_pid = undefined}) -> + ?LOG(warning, "Destroyed", [], State), + shutdown(destroy, State); -%% PUBACK -handle_cast({puback, PktId}, Session = #session{awaiting_ack = AwaitingAck}) -> - case maps:find(PktId, AwaitingAck) of - {ok, TRef} -> - cancel_timer(TRef), - hibernate(dequeue(acked(PktId, Session))); - error -> - ?LOG(warning, "Cannot find PUBACK: ~p", [PktId], Session), - hibernate(Session) - end; - -%% PUBREC -handle_cast({pubrec, PktId}, Session = #session{awaiting_ack = AwaitingAck, - awaiting_comp = AwaitingComp, - await_rel_timeout = Timeout}) -> - case maps:find(PktId, AwaitingAck) of - {ok, TRef} -> - cancel_timer(TRef), - TRef1 = timer(Timeout, {timeout, awaiting_comp, PktId}), - AwaitingComp1 = maps:put(PktId, TRef1, AwaitingComp), - Session1 = acked(PktId, Session#session{awaiting_comp = AwaitingComp1}), - hibernate(dequeue(Session1)); - error -> - ?LOG(error, "Cannot find PUBREC: ~p", [PktId], Session), - hibernate(Session) - end; - -%% PUBREL -handle_cast({pubrel, PktId}, Session = #session{awaiting_rel = AwaitingRel}) -> - case maps:find(PktId, AwaitingRel) of - {ok, {Msg, TRef}} -> - cancel_timer(TRef), - emqttd:publish(Msg), - hibernate(Session#session{awaiting_rel = maps:remove(PktId, AwaitingRel)}); - error -> - ?LOG(error, "Cannot find PUBREL: ~p", [PktId], Session), - hibernate(Session) - end; - -%% PUBCOMP -handle_cast({pubcomp, PktId}, Session = #session{awaiting_comp = AwaitingComp}) -> - case maps:find(PktId, AwaitingComp) of - {ok, TRef} -> - cancel_timer(TRef), - hibernate(Session#session{awaiting_comp = maps:remove(PktId, AwaitingComp)}); - error -> - ?LOG(error, "Cannot find PUBCOMP: ~p", [PktId], Session), - hibernate(Session) - end; +handle_cast({destroy, ClientId}, State = #state{client_id = ClientId, + client_pid = OldClientPid}) -> + ?LOG(warning, "kickout ~p", [OldClientPid], State), + shutdown(conflict, State); handle_cast(Msg, State) -> ?UNEXPECTED_MSG(Msg, State). %% Dispatch Message -handle_info({dispatch, Topic, Msg}, Session = #session{subscriptions = Subscriptions}) - when is_record(Msg, mqtt_message) -> - dispatch(tune_qos(Topic, Msg, Subscriptions), Session); +handle_info({dispatch, Topic, Msg}, State) when is_record(Msg, mqtt_message) -> + noreply(dispatch(tune_qos(Topic, Msg, State), State)); -handle_info({timeout, awaiting_ack, PktId}, Session = #session{client_pid = undefined, - awaiting_ack = AwaitingAck}) -> - %% just remove awaiting - hibernate(Session#session{awaiting_ack = maps:remove(PktId, AwaitingAck)}); +%% Do nothing if the client has been disconnected. +handle_info({timeout, _Timer, retry_delivery}, State = #state{client_pid = undefined}) -> + hibernate(emit_stats(State#state{retry_timer = undefined})); -handle_info({timeout, awaiting_ack, PktId}, Session = #session{inflight_queue = InflightQ, - awaiting_ack = AwaitingAck}) -> - case maps:find(PktId, AwaitingAck) of - {ok, _TRef} -> - case lists:keyfind(PktId, 1, InflightQ) of - {_, Msg} -> - hibernate(redeliver(Msg, Session)); - false -> - ?LOG(error, "AwaitingAck timeout but Cannot find PktId: ~p", [PktId], Session), - hibernate(dequeue(Session)) - end; - error -> - ?LOG(error, "Cannot find AwaitingAck: ~p", [PktId], Session), - hibernate(Session) - end; +handle_info({timeout, _Timer, retry_delivery}, State) -> + noreply(emit_stats(retry_delivery(false, State#state{retry_timer = undefined}))); -handle_info({timeout, awaiting_rel, PktId}, Session = #session{awaiting_rel = AwaitingRel}) -> - case maps:find(PktId, AwaitingRel) of - {ok, {_Msg, _TRef}} -> - ?LOG(warning, "AwaitingRel Timout: ~p, Drop Message!", [PktId], Session), - hibernate(Session#session{awaiting_rel = maps:remove(PktId, AwaitingRel)}); - error -> - ?LOG(error, "Cannot find AwaitingRel: ~p", [PktId], Session), - hibernate(Session) - end; +handle_info({timeout, _Timer, check_awaiting_rel}, State) -> + noreply(expire_awaiting_rel(emit_stats(State#state{await_rel_timer = undefined}))); -handle_info({timeout, awaiting_comp, PktId}, Session = #session{awaiting_comp = Awaiting}) -> - case maps:find(PktId, Awaiting) of - {ok, _TRef} -> - ?LOG(warning, "Awaiting PUBCOMP Timout: ~p", [PktId], Session), - hibernate(Session#session{awaiting_comp = maps:remove(PktId, Awaiting)}); - error -> - ?LOG(error, "Cannot find Awaiting PUBCOMP: ~p", [PktId], Session), - hibernate(Session) - end; +handle_info({timeout, _Timer, emit_stats}, State) -> + hibernate(maybe_enable_stats(emit_stats(State))); -handle_info(collect_info, Session = #session{clean_sess = CleanSess, client_id = ClientId}) -> - emqttd_sm:reg_session(ClientId, CleanSess, sess_info(Session)), - hibernate(start_collector(Session)); +handle_info({timeout, _Timer, expired}, State) -> + ?LOG(info, "Expired, shutdown now.", [], State), + shutdown(expired, State); -handle_info({'EXIT', ClientPid, _Reason}, Session = #session{clean_sess = true, - client_pid = ClientPid}) -> - {stop, normal, Session}; +handle_info({'EXIT', ClientPid, _Reason}, + State = #state{clean_sess = true, client_pid = ClientPid}) -> + {stop, normal, State}; -handle_info({'EXIT', ClientPid, Reason}, Session = #session{clean_sess = false, - client_pid = ClientPid, - expired_after = Expires}) -> - ?LOG(info, "Client ~p EXIT for ~p", [ClientPid, Reason], Session), - TRef = timer(Expires, expired), - hibernate(Session#session{client_pid = undefined, expired_timer = TRef}); +handle_info({'EXIT', ClientPid, Reason}, + State = #state{clean_sess = false, + client_pid = ClientPid, + expiry_interval = Interval}) -> + ?LOG(info, "Client ~p EXIT for ~p", [ClientPid, Reason], State), + ExpireTimer = start_timer(Interval, expired), + State1 = State#state{client_pid = undefined, expiry_timer = ExpireTimer}, + hibernate(maybe_enable_stats(emit_stats(State1))); -handle_info({'EXIT', Pid, _Reason}, Session = #session{old_client_pid = Pid}) -> +handle_info({'EXIT', Pid, _Reason}, State = #state{old_client_pid = Pid}) -> %%ignore - hibernate(Session); + hibernate(State); -handle_info({'EXIT', Pid, Reason}, Session = #session{client_pid = ClientPid}) -> +handle_info({'EXIT', Pid, Reason}, State = #state{client_pid = ClientPid}) -> ?LOG(error, "Unexpected EXIT: client_pid=~p, exit_pid=~p, reason=~p", - [ClientPid, Pid, Reason], Session), - hibernate(Session); - -handle_info(expired, Session) -> - ?LOG(info, "expired, shutdown now.", [], Session), - shutdown(expired, Session); + [ClientPid, Pid, Reason], State), + hibernate(State); handle_info(Info, Session) -> ?UNEXPECTED_INFO(Info, Session). -terminate(Reason, #session{client_id = ClientId, username = Username}) -> - emqttd:run_hooks('session.terminated', [ClientId, Username, Reason]), - emqttd:subscriber_down(ClientId), - emqttd_sm:unreg_session(ClientId). +terminate(Reason, #state{client_id = ClientId, username = Username}) -> + emqttd_stats:del_session_stats(ClientId), + emqttd_hooks:run('session.terminated', [ClientId, Username, Reason]), + emqttd_server:subscriber_down(ClientId), + emqttd_sm:unregister_session(ClientId). code_change(_OldVsn, Session, _Extra) -> {ok, Session}. %%-------------------------------------------------------------------- -%% Kick old client out +%% Kick old client %%-------------------------------------------------------------------- kick(_ClientId, undefined, _Pid) -> ignore; @@ -546,130 +585,228 @@ kick(ClientId, OldPid, Pid) -> %% Clean noproc receive {'EXIT', OldPid, _} -> ok after 0 -> ok end. +%%-------------------------------------------------------------------- +%% Replay or Retry Delivery +%%-------------------------------------------------------------------- + +%% Redeliver at once if Force is true + +retry_delivery(Force, State = #state{inflight = Inflight}) -> + case Inflight:is_empty() of + true -> State; + false -> Msgs = lists:sort(sortfun(inflight), Inflight:values()), + retry_delivery(Force, Msgs, os:timestamp(), State) + end. + +retry_delivery(_Force, [], _Now, State = #state{retry_interval = Interval}) -> + State#state{retry_timer = start_timer(Interval, retry_delivery)}; + +retry_delivery(Force, [{Type, Msg, Ts} | Msgs], Now, + State = #state{inflight = Inflight, + retry_interval = Interval}) -> + Diff = timer:now_diff(Now, Ts) div 1000, %% micro -> ms + if + Force orelse (Diff >= Interval) -> + case {Type, Msg} of + {publish, Msg = #mqtt_message{pktid = PacketId}} -> + redeliver(Msg, State), + Inflight1 = Inflight:update(PacketId, {publish, Msg, Now}), + retry_delivery(Force, Msgs, Now, State#state{inflight = Inflight1}); + {pubrel, PacketId} -> %% remove 'pubrel' directly? + retry_delivery(Force, Msgs, Now, State#state{inflight = Inflight:delete(PacketId)}) + end; + true -> + State#state{retry_timer = start_timer(Interval - Diff, retry_delivery)} + end. + +%%-------------------------------------------------------------------- +%% Expire Awaiting Rel +%%-------------------------------------------------------------------- + +expire_awaiting_rel(State = #state{awaiting_rel = AwaitingRel}) -> + case maps:size(AwaitingRel) of + 0 -> State; + _ -> Msgs = lists:sort(sortfun(awaiting_rel), maps:to_list(AwaitingRel)), + expire_awaiting_rel(Msgs, os:timestamp(), State) + end. + +expire_awaiting_rel([], _Now, State) -> + State#state{await_rel_timer = undefined}; + +expire_awaiting_rel([{PacketId, #mqtt_message{timestamp = TS}} | Msgs], + Now, State = #state{awaiting_rel = AwaitingRel, + await_rel_timeout = Timeout}) -> + case (timer:now_diff(Now, TS) div 1000) of + Diff when Diff >= Timeout -> + expire_awaiting_rel(Msgs, Now, State#state{awaiting_rel = maps:remove(PacketId, AwaitingRel)}); + Diff -> + State#state{await_rel_timer = start_timer(Timeout - Diff, check_awaiting_rel)} + end. + +%%-------------------------------------------------------------------- +%% Sort Inflight, AwaitingRel +%%-------------------------------------------------------------------- + +sortfun(inflight) -> + fun({_, _, Ts1}, {_, _, Ts2}) -> Ts1 < Ts2 end; + +sortfun(awaiting_rel) -> + fun({_, #mqtt_message{timestamp = Ts1}}, + {_, #mqtt_message{timestamp = Ts2}}) -> + Ts1 < Ts2 + end. + +%%-------------------------------------------------------------------- +%% Check awaiting rel +%%-------------------------------------------------------------------- + +is_awaiting_full(#state{max_awaiting_rel = 0}) -> + false; +is_awaiting_full(#state{awaiting_rel = AwaitingRel, max_awaiting_rel = MaxLen}) -> + maps:size(AwaitingRel) >= MaxLen. + %%-------------------------------------------------------------------- %% Dispatch Messages %%-------------------------------------------------------------------- -%% Queue message if client disconnected -dispatch(Msg, Session = #session{client_pid = undefined, message_queue = Q}) -> - hibernate(Session#session{message_queue = emqttd_mqueue:in(Msg, Q)}); +%% Enqueue message if the client has been disconnected +dispatch(Msg, State = #state{client_pid = undefined}) -> + enqueue_msg(Msg, State); %% Deliver qos0 message directly to client -dispatch(Msg = #mqtt_message{qos = ?QOS0}, Session = #session{client_pid = ClientPid}) -> - ClientPid ! {deliver, Msg}, - hibernate(Session); +dispatch(Msg = #mqtt_message{qos = ?QOS0}, State) -> + deliver(Msg, State), State; -dispatch(Msg = #mqtt_message{qos = QoS}, Session = #session{message_queue = MsgQ}) +dispatch(Msg = #mqtt_message{qos = QoS}, + State = #state{next_msg_id = MsgId, inflight = Inflight}) when QoS =:= ?QOS1 orelse QoS =:= ?QOS2 -> - case check_inflight(Session) of + case Inflight:is_full() of true -> - noreply(deliver(Msg, Session)); + enqueue_msg(Msg, State); false -> - hibernate(Session#session{message_queue = emqttd_mqueue:in(Msg, MsgQ)}) + Msg1 = Msg#mqtt_message{pktid = MsgId}, + deliver(Msg1, State), + await(Msg1, next_msg_id(State)) end. -tune_qos(Topic, Msg = #mqtt_message{qos = PubQos}, SubMap) -> +enqueue_msg(Msg, State = #state{mqueue = Q}) -> + inc(enqueue_msg), State#state{mqueue = emqttd_mqueue:in(Msg, Q)}. + +%%-------------------------------------------------------------------- +%% Deliver +%%-------------------------------------------------------------------- + +redeliver(Msg = #mqtt_message{qos = QoS}, State) -> + deliver(Msg#mqtt_message{dup = if QoS =:= ?QOS2 -> false; true -> true end}, State). + +deliver(Msg, #state{client_pid = Pid}) -> + inc(deliver_msg), Pid ! {deliver, Msg}. + +%%-------------------------------------------------------------------- +%% Awaiting ACK for QoS1/QoS2 Messages +%%-------------------------------------------------------------------- + +await(Msg = #mqtt_message{pktid = PacketId}, + State = #state{inflight = Inflight, + retry_timer = RetryTimer, + retry_interval = Interval}) -> + %% Start retry timer if the Inflight is still empty + State1 = ?IF(RetryTimer == undefined, State#state{retry_timer = start_timer(Interval, retry_delivery)}, State), + State1#state{inflight = Inflight:insert(PacketId, {publish, Msg, os:timestamp()})}. + +acked(puback, PacketId, State = #state{client_id = ClientId, + username = Username, + inflight = Inflight}) -> + {publish, Msg, _Ts} = Inflight:lookup(PacketId), + emqttd_hooks:run('message.acked', [ClientId, Username], Msg), + State#state{inflight = Inflight:delete(PacketId)}; + +acked(pubrec, PacketId, State = #state{client_id = ClientId, + username = Username, + inflight = Inflight}) -> + {publish, Msg, _Ts} = Inflight:lookup(PacketId), + emqttd_hooks:run('message.acked', [ClientId, Username], Msg), + State#state{inflight = Inflight:update(PacketId, {pubrel, PacketId, os:timestamp()})}; + +acked(pubcomp, PacketId, State = #state{inflight = Inflight}) -> + State#state{inflight = Inflight:delete(PacketId)}. + +%%-------------------------------------------------------------------- +%% Dequeue +%%-------------------------------------------------------------------- + +%% Do nothing if client is disconnected +dequeue(State = #state{client_pid = undefined}) -> + State; + +dequeue(State = #state{inflight = Inflight}) -> + case Inflight:is_full() of + true -> State; + false -> dequeue2(State) + end. + +dequeue2(State = #state{mqueue = Q}) -> + case emqttd_mqueue:out(Q) of + {empty, _Q} -> + State; + {{value, Msg}, Q1} -> + %% Dequeue more + dequeue(dispatch(Msg, State#state{mqueue = Q1})) + end. + +%%-------------------------------------------------------------------- +%% Tune QoS +%%-------------------------------------------------------------------- + +tune_qos(Topic, Msg = #mqtt_message{qos = PubQoS}, + #state{subscriptions = SubMap, upgrade_qos = UpgradeQoS}) -> case maps:find(Topic, SubMap) of - {ok, SubQos} when PubQos > SubQos -> - Msg#mqtt_message{qos = SubQos}; - {ok, _SubQos} -> + {ok, SubQoS} when UpgradeQoS andalso (SubQoS > PubQoS) -> + Msg#mqtt_message{qos = SubQoS}; + {ok, SubQoS} when (not UpgradeQoS) andalso (SubQoS < PubQoS) -> + Msg#mqtt_message{qos = SubQoS}; + {ok, _} -> Msg; error -> Msg end. %%-------------------------------------------------------------------- -%% Check inflight and awaiting_rel +%% Next Msg Id %%-------------------------------------------------------------------- -check_inflight(#session{max_inflight = 0}) -> - true; -check_inflight(#session{max_inflight = Max, inflight_queue = Q}) -> - Max > length(Q). +next_msg_id(State = #state{next_msg_id = 16#FFFF}) -> + State#state{next_msg_id = 1}; -check_awaiting_rel(#session{max_awaiting_rel = 0}) -> - true; -check_awaiting_rel(#session{awaiting_rel = AwaitingRel, - max_awaiting_rel = MaxLen}) -> - maps:size(AwaitingRel) < MaxLen. +next_msg_id(State = #state{next_msg_id = Id}) -> + State#state{next_msg_id = Id + 1}. %%-------------------------------------------------------------------- -%% Dequeue and Deliver +%% Emit session stats %%-------------------------------------------------------------------- -dequeue(Session = #session{client_pid = undefined}) -> - %% do nothing if client is disconnected - Session; +maybe_enable_stats(State = #state{enable_stats = false}) -> + State; +maybe_enable_stats(State = #state{client_pid = Pid}) when is_pid(Pid) -> + State; +maybe_enable_stats(State = #state{enable_stats = Interval}) -> + StatsTimer = start_timer(Interval, emit_stats), + State#state{stats_timer = StatsTimer}. -dequeue(Session) -> - case check_inflight(Session) of - true -> dequeue2(Session); - false -> Session - end. - -dequeue2(Session = #session{message_queue = Q}) -> - case emqttd_mqueue:out(Q) of - {empty, _Q} -> - Session; - {{value, Msg}, Q1} -> - %% dequeue more - dequeue(deliver(Msg, Session#session{message_queue = Q1})) - end. - -deliver(Msg = #mqtt_message{qos = ?QOS_0}, Session = #session{client_pid = ClientPid}) -> - ClientPid ! {deliver, Msg}, Session; - -deliver(Msg = #mqtt_message{qos = QoS}, Session = #session{packet_id = PktId, - client_pid = ClientPid, - inflight_queue = InflightQ}) - when QoS =:= ?QOS_1 orelse QoS =:= ?QOS_2 -> - Msg1 = Msg#mqtt_message{pktid = PktId, dup = false}, - ClientPid ! {deliver, Msg1}, - await(Msg1, next_packet_id(Session#session{inflight_queue = [{PktId, Msg1}|InflightQ]})). - -redeliver(Msg = #mqtt_message{qos = ?QOS_0}, Session) -> - deliver(Msg, Session); - -redeliver(Msg = #mqtt_message{qos = QoS}, Session = #session{client_pid = ClientPid}) - when QoS =:= ?QOS_1 orelse QoS =:= ?QOS_2 -> - ClientPid ! {deliver, Msg#mqtt_message{dup = true}}, - await(Msg, Session). +emit_stats(State = #state{enable_stats = false}) -> + State; +emit_stats(State = #state{client_id = ClientId}) -> + emqttd_stats:set_session_stats(ClientId, stats(State)), + State. %%-------------------------------------------------------------------- -%% Awaiting ack for qos1, qos2 message -%%------------------------------------------------------------------------------ -await(#mqtt_message{pktid = PktId}, Session = #session{awaiting_ack = Awaiting, - retry_interval = Timeout}) -> - TRef = timer(Timeout, {timeout, awaiting_ack, PktId}), - Awaiting1 = maps:put(PktId, TRef, Awaiting), - Session#session{awaiting_ack = Awaiting1}. +%% Helper functions +%%-------------------------------------------------------------------- -acked(PktId, Session = #session{client_id = ClientId, - username = Username, - inflight_queue = InflightQ, - awaiting_ack = Awaiting}) -> - case lists:keyfind(PktId, 1, InflightQ) of - {_, Msg} -> - emqttd:run_hooks('message.acked', [ClientId, Username], Msg); - false -> - ?LOG(error, "Cannot find acked pktid: ~p", [PktId], Session) - end, - Session#session{awaiting_ack = maps:remove(PktId, Awaiting), - inflight_queue = lists:keydelete(PktId, 1, InflightQ)}. +inc(Key) -> put(Key, get(Key) + 1). -next_packet_id(Session = #session{packet_id = 16#ffff}) -> - Session#session{packet_id = 1}; - -next_packet_id(Session = #session{packet_id = Id}) -> - Session#session{packet_id = Id + 1}. - -timer(TimeoutSec, TimeoutMsg) -> - erlang:send_after(timer:seconds(TimeoutSec), self(), TimeoutMsg). - -cancel_timer(undefined) -> - undefined; -cancel_timer(Ref) -> - catch erlang:cancel_timer(Ref). +reply(Reply, State) -> + {reply, Reply, State}. noreply(State) -> {noreply, State}. @@ -680,29 +817,3 @@ hibernate(State) -> shutdown(Reason, State) -> {stop, {shutdown, Reason}, State}. -start_collector(Session = #session{collect_interval = 0}) -> - Session; - -start_collector(Session = #session{collect_interval = Interval}) -> - TRef = erlang:send_after(timer:seconds(Interval), self(), collect_info), - Session#session{collect_timer = TRef}. - -sess_info(#session{clean_sess = CleanSess, - inflight_queue = InflightQueue, - max_inflight = MaxInflight, - message_queue = MessageQueue, - awaiting_rel = AwaitingRel, - awaiting_ack = AwaitingAck, - awaiting_comp = AwaitingComp, - timestamp = CreatedAt}) -> - Stats = emqttd_mqueue:stats(MessageQueue), - [{clean_sess, CleanSess}, - {max_inflight, MaxInflight}, - {inflight_queue, length(InflightQueue)}, - {message_queue, get_value(len, Stats)}, - {message_dropped,get_value(dropped, Stats)}, - {awaiting_rel, maps:size(AwaitingRel)}, - {awaiting_ack, maps:size(AwaitingAck)}, - {awaiting_comp, maps:size(AwaitingComp)}, - {created_at, CreatedAt}]. - From a5ba86fd1a9b845ab87799f5032db7df94160704 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Thu, 16 Feb 2017 11:46:21 +0800 Subject: [PATCH 041/125] Update rebar.config --- rebar.config | 5 +---- src/emqttd_router.erl | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/rebar.config b/rebar.config index af42c962a..3ac69aea2 100644 --- a/rebar.config +++ b/rebar.config @@ -1,7 +1,4 @@ {deps, [ - {gproc,".*",{git,"https://github.com/uwiger/gproc",""}}, - {lager,".*",{git,"https://github.com/basho/lager","master"}}, - {esockd,".*",{git,"https://github.com/emqtt/esockd","master"}}, - {mochiweb,".*",{git,"https://github.com/emqtt/mochiweb",""}} +{gproc,".*",{git,"https://github.com/uwiger/gproc",""}},{lager,".*",{git,"https://github.com/basho/lager","master"}},{esockd,".*",{git,"https://github.com/emqtt/esockd","master"}},{mochiweb,".*",{git,"https://github.com/emqtt/mochiweb",""}} ]}. {erl_opts, [{parse_transform,lager_transform}]}. diff --git a/src/emqttd_router.erl b/src/emqttd_router.erl index e0e3962a2..6894ea528 100644 --- a/src/emqttd_router.erl +++ b/src/emqttd_router.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. From fde1f92bf2031ca3d74b70cad23fcf1b31cada85 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Thu, 16 Feb 2017 11:49:59 +0800 Subject: [PATCH 042/125] Fix format of CT_SUITES --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 4355cc413..3711319dd 100644 --- a/Makefile +++ b/Makefile @@ -25,8 +25,8 @@ EUNIT_OPTS = verbose # EUNIT_ERL_OPTS = CT_SUITES = emqttd emqttd_access emqttd_lib emqttd_inflight emqttd_mod \ - emqttd_net emqttd_mqueue emqttd_protocol emqttd_topic \ - emqttd_trie emqttd_vm + emqttd_net emqttd_mqueue emqttd_protocol emqttd_topic \ + emqttd_trie emqttd_vm CT_OPTS = -cover test/ct.cover.spec -erl_args -name emqttd_ct@127.0.0.1 From 5419266724a1ce28c42a75d6db76414e31df56c7 Mon Sep 17 00:00:00 2001 From: turtled Date: Thu, 16 Feb 2017 18:33:57 +0800 Subject: [PATCH 043/125] emqttd_hook -> emqttd_hooks, syslog --- Makefile | 3 ++- src/emqttd_app.erl | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 3711319dd..1447dd30c 100644 --- a/Makefile +++ b/Makefile @@ -2,13 +2,14 @@ PROJECT = emqttd PROJECT_DESCRIPTION = Erlang MQTT Broker PROJECT_VERSION = 2.1 -DEPS = gproc lager esockd mochiweb +DEPS = gproc lager esockd mochiweb lager_syslog dep_gproc = git https://github.com/uwiger/gproc dep_getopt = git https://github.com/jcomellas/getopt v0.8.2 dep_lager = git https://github.com/basho/lager master dep_esockd = git https://github.com/emqtt/esockd master dep_mochiweb = git https://github.com/emqtt/mochiweb +dep_lager_syslog = git https://github.com/basho/lager_syslog ERLC_OPTS += +'{parse_transform, lager_transform}' diff --git a/src/emqttd_app.erl b/src/emqttd_app.erl index 6f758b870..992e22da6 100644 --- a/src/emqttd_app.erl +++ b/src/emqttd_app.erl @@ -72,7 +72,7 @@ print_vsn() -> start_servers(Sup) -> Servers = [{"emqttd ctl", emqttd_ctl}, - {"emqttd hook", emqttd_hook}, + {"emqttd hook", emqttd_hooks}, {"emqttd router", emqttd_router}, {"emqttd pubsub", {supervisor, emqttd_pubsub_sup}}, {"emqttd stats", emqttd_stats}, From 726f829df5d68d4be2559085e66fb466ec28a9d0 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Thu, 16 Feb 2017 18:59:53 +0800 Subject: [PATCH 044/125] Fix the error caused by emqttd:env/1 --- src/emqttd_ws_client_sup.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/emqttd_ws_client_sup.erl b/src/emqttd_ws_client_sup.erl index 60e1461b7..1375ac036 100644 --- a/src/emqttd_ws_client_sup.erl +++ b/src/emqttd_ws_client_sup.erl @@ -39,7 +39,7 @@ start_client(WsPid, Req, ReplyChannel) -> %%-------------------------------------------------------------------- init([]) -> - Env = lists:append(emqttd:env(client), emqttd:env(protocol)), + Env = lists:append(emqttd:env(client, []), emqttd:env(protocol, [])), {ok, {{simple_one_for_one, 0, 1}, [{ws_client, {emqttd_ws_client, start_link, [Env]}, temporary, 5000, worker, [emqttd_ws_client]}]}}. From 8f702babbeeb4d09207ded68daf722a533377246 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Fri, 17 Feb 2017 16:56:04 +0800 Subject: [PATCH 045/125] Add emq_retainer, emq_modules plugins --- README.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 95a0a0d54..8dc7ea803 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,8 @@ Plugin | Descrip -----------------------------------------------------------------------|-------------------------------------- [emq_plugin_template](https://github.com/emqtt/emq_plugin_template) | Plugin template and demo [emq_dashboard](https://github.com/emqtt/emq_dashboard) | Web Dashboard +[emq_retainer](https://github.com/emqtt/emq_retainer) | Store MQTT Retained Messages +[emq_modules](https://github.com/emqtt/emq_modules) | Presence, Subscription and Rewrite Modules [emq_auth_username](https://github.com/emqtt/emq_auth_username) | Username/Password Authentication Plugin [emq_auth_clientid](https://github.com/emqtt/emq_auth_clientid) | ClientId Authentication Plugin [emq_auth_mysql](https://github.com/emqtt/emq_auth_mysql) | MySQL Authentication/ACL Plugin @@ -79,10 +81,6 @@ Plugin | Descrip [emq_auth_mongo](https://github.com/emqtt/emq_auth_mongo) | MongoDB Authentication/ACL Plugin [emq_auth_http](https://github.com/emqtt/emq_auth_http) | Authentication/ACL by HTTP API [emq_auth_ldap](https://github.com/emqtt/emq_auth_ldap) | LDAP Authentication Plugin -[emq_mod_presence](https://github.com/emqtt/emq_mod_presence) | Presence Module -[emq_mod_rewrite](https://github.com/emqtt/emq_mod_rewrite) | Rewrite Module -[emq_mod_retainer](https://github.com/emqtt/emq_mod_retainer) | Store MQTT Retained Messages -[emq_mod_subscription](https://github.com/emqtt/emq_mod_subscription) | Subscribe topics when client connected [emq_sn](https://github.com/emqtt/emq_sn) | MQTT-SN Protocol Plugin [emq_coap](https://github.com/emqtt/emq_coap) | CoAP Protocol Plugin [emq_stomp](https://github.com/emqtt/emq_stomp) | Stomp Protocol Plugin From 4a41d005cb3b6607fb76ae598ed53a340d81f7dd Mon Sep 17 00:00:00 2001 From: huangdan Date: Fri, 17 Feb 2017 17:12:27 +0800 Subject: [PATCH 046/125] Fix tests --- test/emqttd_SUITE.erl | 10 +++++----- test/emqttd_lib_SUITE.erl | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/emqttd_SUITE.erl b/test/emqttd_SUITE.erl index a7ab2fb5f..d7af619df 100644 --- a/test/emqttd_SUITE.erl +++ b/test/emqttd_SUITE.erl @@ -371,21 +371,21 @@ add_delete_hook(_) -> {error, already_hooked} = emqttd:hook(test_hook, fun ?MODULE:hook_fun2/1, []), Callbacks = [{callback, fun ?MODULE:hook_fun1/1, [], 0}, {callback, fun ?MODULE:hook_fun2/1, [], 0}], - Callbacks = emqttd_hook:lookup(test_hook), + Callbacks = emqttd_hooks:lookup(test_hook), emqttd:unhook(test_hook, fun ?MODULE:hook_fun1/1), emqttd:unhook(test_hook, fun ?MODULE:hook_fun2/1), ok = emqttd:unhook(test_hook, fun ?MODULE:hook_fun2/1), {error, not_found} = emqttd:unhook(test_hook1, fun ?MODULE:hook_fun2/1), - [] = emqttd_hook:lookup(test_hook), + [] = emqttd_hooks:lookup(test_hook), emqttd:hook(emqttd_hook, fun ?MODULE:hook_fun1/1, [], 9), emqttd:hook(emqttd_hook, fun ?MODULE:hook_fun2/1, [], 8), Callbacks2 = [{callback, fun ?MODULE:hook_fun2/1, [], 8}, {callback, fun ?MODULE:hook_fun1/1, [], 9}], - Callbacks2 = emqttd_hook:lookup(emqttd_hook), + Callbacks2 = emqttd_hooks:lookup(emqttd_hook), emqttd:unhook(emqttd_hook, fun ?MODULE:hook_fun1/1), emqttd:unhook(emqttd_hook, fun ?MODULE:hook_fun2/1), - [] = emqttd_hook:lookup(emqttd_hook). + [] = emqttd_hooks:lookup(emqttd_hook). run_hooks(_) -> emqttd:hook(foldl_hook, fun ?MODULE:hook_fun3/4, [init]), @@ -514,8 +514,8 @@ cluster_remove2(_) -> ok = emqttd_cluster:join(Z), Node = node(), [Z, Node] = emqttd_mnesia:running_nodes(), - ok = rpc:call(Z, emqttd_mnesia, ensure_stopped, []), ok = emqttd_cluster:remove(Z), + ok = rpc:call(Z, emqttd_mnesia, ensure_stopped, []), [Node] = emqttd_mnesia:running_nodes(), slave:stop(Z). diff --git a/test/emqttd_lib_SUITE.erl b/test/emqttd_lib_SUITE.erl index d4d44cb82..c16858785 100644 --- a/test/emqttd_lib_SUITE.erl +++ b/test/emqttd_lib_SUITE.erl @@ -71,7 +71,7 @@ guid_base62(_) -> %%-------------------------------------------------------------------- opts_merge(_) -> - Opts = emqttd_opts:merge(?SOCKOPTS, [raw, + Opts = emqttd_misc:merge_opts(?SOCKOPTS, [raw, binary, {backlog, 1024}, {nodelay, false}, From 79217fdade97ea3ecc0dda97830525e81b132679 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Sat, 18 Feb 2017 14:14:24 +0800 Subject: [PATCH 047/125] Remove the 'infinity' timeout --- src/emqttd_client.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/emqttd_client.erl b/src/emqttd_client.erl index 1e4b3db4a..10c6f6edc 100644 --- a/src/emqttd_client.erl +++ b/src/emqttd_client.erl @@ -65,7 +65,7 @@ start_link(Conn, Env) -> {ok, proc_lib:spawn_link(?MODULE, init, [[Conn, Env]])}. info(CPid) -> - gen_server:call(CPid, info, infinity). + gen_server:call(CPid, info). stats(CPid) -> gen_server:call(CPid, stats). From a7ed2c402b57868b83c2fc49a243bc5453b0fef6 Mon Sep 17 00:00:00 2001 From: turtled Date: Sat, 18 Feb 2017 15:03:41 +0800 Subject: [PATCH 048/125] fix #885 --- src/emqttd_acl_internal.erl | 3 ++- src/emqttd_cli.erl | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/emqttd_acl_internal.erl b/src/emqttd_acl_internal.erl index f1c8b801b..1cd32c0f4 100644 --- a/src/emqttd_acl_internal.erl +++ b/src/emqttd_acl_internal.erl @@ -21,6 +21,7 @@ -author("Feng Lee "). -include("emqttd.hrl"). +-include("emqttd_cli.hrl"). -export([all_rules/0]). @@ -114,7 +115,7 @@ reload_acl(#state{config = undefined}) -> reload_acl(State) -> case catch load_rules_from_file(State) of {'EXIT', Error} -> {error, Error}; - true -> ok + true -> ?PRINT("~s~n", ["reload acl_internal successfully"]), ok end. %% @doc ACL Module Description diff --git a/src/emqttd_cli.erl b/src/emqttd_cli.erl index 5a4de3fab..cb4438cc8 100644 --- a/src/emqttd_cli.erl +++ b/src/emqttd_cli.erl @@ -32,7 +32,7 @@ -export([status/1, broker/1, cluster/1, users/1, clients/1, sessions/1, routes/1, topics/1, subscriptions/1, plugins/1, bridges/1, - listeners/1, vm/1, mnesia/1, trace/1]). + listeners/1, vm/1, mnesia/1, trace/1, acl/1]). -define(PROC_INFOKEYS, [status, memory, @@ -151,6 +151,9 @@ cluster(_) -> users(Args) -> emq_auth_username:cli(Args). +acl(["reload"]) -> emqttd_access_control:reload_acl(); +acl(_) -> ?USAGE([{"acl reload", "reload etc/acl.conf"}]). + %%-------------------------------------------------------------------- %% @doc Query clients From cabd1af23aba44c77ffdda4a5b8f79086f5d8816 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Mon, 20 Feb 2017 16:45:53 +0800 Subject: [PATCH 049/125] Hiberate after subscribe, unsubscribe and resume --- src/emqttd_session.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/emqttd_session.erl b/src/emqttd_session.erl index 31efaab55..eb3756460 100644 --- a/src/emqttd_session.erl +++ b/src/emqttd_session.erl @@ -391,7 +391,7 @@ handle_cast({subscribe, _From, TopicTable, AckFun}, {[NewQos|QosAcc], SubMap1} end, {[], Subscriptions}, TopicTable), AckFun(lists:reverse(GrantedQos)), - noreply(emit_stats(State#state{subscriptions = Subscriptions1})); + hibernate(emit_stats(State#state{subscriptions = Subscriptions1})); handle_cast({unsubscribe, _From, TopicTable}, State = #state{client_id = ClientId, @@ -409,7 +409,7 @@ handle_cast({unsubscribe, _From, TopicTable}, SubMap end end, Subscriptions, TopicTable), - noreply(emit_stats(State#state{subscriptions = Subscriptions1})); + hibernate(emit_stats(State#state{subscriptions = Subscriptions1})); %% PUBACK: handle_cast({puback, PacketId}, State = #state{inflight = Inflight}) -> @@ -501,7 +501,7 @@ handle_cast({resume, ClientId, ClientPid}, end, %% Replay delivery and Dequeue pending messages - noreply(emit_stats(dequeue(retry_delivery(true, State1)))); + hibernate(emit_stats(dequeue(retry_delivery(true, State1)))); handle_cast({destroy, ClientId}, State = #state{client_id = ClientId, client_pid = undefined}) -> From 3cdf2377c8d69374770c42870a1a466d5f158a81 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Tue, 21 Feb 2017 18:45:40 +0800 Subject: [PATCH 050/125] Use gen_server2 to reduce the CPU/memory usage --- src/emqttd_client.erl | 95 +++++++++++++------------ src/emqttd_misc.erl | 4 +- src/emqttd_protocol.erl | 2 + src/emqttd_session.erl | 150 ++++++++++++++++++--------------------- src/emqttd_ws_client.erl | 86 ++++++++++++---------- 5 files changed, 174 insertions(+), 163 deletions(-) diff --git a/src/emqttd_client.erl b/src/emqttd_client.erl index 10c6f6edc..b48e12f39 100644 --- a/src/emqttd_client.erl +++ b/src/emqttd_client.erl @@ -18,7 +18,7 @@ -module(emqttd_client). --behaviour(gen_server). +-behaviour(gen_server2). -author("Feng Lee "). @@ -48,11 +48,13 @@ -export([init/1, handle_call/3, handle_cast/2, handle_info/2, code_change/3, terminate/2]). +%% gen_server2 Callbacks +-export([prioritise_call/4, prioritise_info/3, handle_pre_hibernate/1]). + %% Client State -record(client_state, {connection, connname, peername, peerhost, peerport, await_recv, conn_state, rate_limit, parser_fun, - proto_state, packet_opts, keepalive, enable_stats, - stats_timer}). + proto_state, packet_opts, keepalive, enable_stats}). -define(INFO_KEYS, [connname, peername, peerhost, peerport, await_recv, conn_state]). @@ -65,19 +67,19 @@ start_link(Conn, Env) -> {ok, proc_lib:spawn_link(?MODULE, init, [[Conn, Env]])}. info(CPid) -> - gen_server:call(CPid, info). + gen_server2:call(CPid, info). stats(CPid) -> - gen_server:call(CPid, stats). + gen_server2:call(CPid, stats). kick(CPid) -> - gen_server:call(CPid, kick). + gen_server2:call(CPid, kick). set_rate_limit(Cpid, Rl) -> - gen_server:call(Cpid, {set_rate_limit, Rl}). + gen_server2:call(Cpid, {set_rate_limit, Rl}). get_rate_limit(Cpid) -> - gen_server:call(Cpid, get_rate_limit). + gen_server2:call(Cpid, get_rate_limit). subscribe(CPid, TopicTable) -> CPid ! {subscribe, TopicTable}. @@ -135,30 +137,41 @@ init([Conn0, Env]) -> packet_opts = Env, enable_stats = EnableStats}), IdleTimout = get_value(client_idle_timeout, Env, 30000), - gen_server:enter_loop(?MODULE, [], maybe_enable_stats(State), IdleTimout). + gen_server2:enter_loop(?MODULE, [], State, self(), IdleTimout, + {backoff, 1000, 1000, 5000}). + +prioritise_call(Msg, _From, _Len, _State) -> + case Msg of info -> 10; stats -> 10; state -> 10; _ -> 5 end. + +prioritise_info(Msg, _Len, _State) -> + case Msg of {redeliver, _} -> 5; _ -> 0 end. + +handle_pre_hibernate(State = #client_state{connname = Connname}) -> + io:format("Client(~s) will hibernate!~n", [Connname]), + {hibernate, emit_stats(State)}. handle_call(info, From, State = #client_state{proto_state = ProtoState}) -> ProtoInfo = emqttd_protocol:info(ProtoState), ClientInfo = ?record_to_proplist(client_state, State, ?INFO_KEYS), - {reply, Stats, _} = handle_call(stats, From, State), - {reply, lists:append([ClientInfo, ProtoInfo, Stats]), State}; + {reply, Stats, _, _} = handle_call(stats, From, State), + reply(lists:append([ClientInfo, ProtoInfo, Stats]), State); handle_call(stats, _From, State = #client_state{proto_state = ProtoState}) -> - {reply, lists:append([emqttd_misc:proc_stats(), - emqttd_protocol:stats(ProtoState), - sock_stats(State)]), State}; + reply(lists:append([emqttd_misc:proc_stats(), + emqttd_protocol:stats(ProtoState), + sock_stats(State)]), State); handle_call(kick, _From, State) -> {stop, {shutdown, kick}, ok, State}; handle_call({set_rate_limit, Rl}, _From, State) -> - {reply, ok, State#client_state{rate_limit = Rl}}; + reply(ok, State#client_state{rate_limit = Rl}); handle_call(get_rate_limit, _From, State = #client_state{rate_limit = Rl}) -> - {reply, Rl, State}; + reply(Rl, State); handle_call(session, _From, State = #client_state{proto_state = ProtoState}) -> - {reply, emqttd_protocol:session(ProtoState), State}; + reply(emqttd_protocol:session(ProtoState), State); handle_call(Req, _From, State) -> ?UNEXPECTED_REQ(Req, State). @@ -198,12 +211,12 @@ handle_info({redeliver, {?PUBREL, PacketId}}, State) -> emqttd_protocol:pubrel(PacketId, ProtoState) end, State); +handle_info(emit_stats, State) -> + {noreply, emit_stats(State), hibernate}; + handle_info(timeout, State) -> shutdown(idle_timeout, State); -handle_info({timeout, _Timer, emit_stats}, State) -> - hibernate(maybe_enable_stats(emit_stats(State))); - %% Fix issue #535 handle_info({shutdown, Error}, State) -> shutdown(Error, State); @@ -213,7 +226,7 @@ handle_info({shutdown, conflict, {ClientId, NewPid}}, State) -> shutdown(conflict, State); handle_info(activate_sock, State) -> - hibernate(run_socket(State#client_state{conn_state = running})); + {noreply, run_socket(State#client_state{conn_state = running}), hibernate}; handle_info({inet_async, _Sock, _Ref, {ok, Data}}, State) -> Size = iolist_size(Data), @@ -239,12 +252,12 @@ handle_info({keepalive, start, Interval}, State = #client_state{connection = Con end end, KeepAlive = emqttd_keepalive:start(StatFun, Interval, {keepalive, check}), - {noreply, stats_by_keepalive(State#client_state{keepalive = KeepAlive})}; + {noreply, State#client_state{keepalive = KeepAlive}, hibernate}; handle_info({keepalive, check}, State = #client_state{keepalive = KeepAlive}) -> case emqttd_keepalive:check(KeepAlive) of {ok, KeepAlive1} -> - hibernate(emit_stats(State#client_state{keepalive = KeepAlive1})); + {noreply, State#client_state{keepalive = KeepAlive1}, hibernate}; {error, timeout} -> ?LOG(debug, "Keepalive timeout", [], State), shutdown(keepalive_timeout, State); @@ -279,7 +292,7 @@ code_change(_OldVsn, State, _Extra) -> %% Receive and parse tcp data received(<<>>, State) -> - hibernate(State); + {noreply, State, hibernate}; received(Bytes, State = #client_state{parser_fun = ParserFun, packet_opts = PacketOpts, @@ -332,33 +345,25 @@ run_socket(State = #client_state{connection = Conn}) -> with_proto(Fun, State = #client_state{proto_state = ProtoState}) -> {ok, ProtoState1} = Fun(ProtoState), - {noreply, State#client_state{proto_state = ProtoState1}}. + {noreply, State#client_state{proto_state = ProtoState1}, hibernate}. -maybe_enable_stats(State = #client_state{enable_stats = false}) -> - State; -maybe_enable_stats(State = #client_state{enable_stats = keepalive}) -> - State; -maybe_enable_stats(State = #client_state{enable_stats = Interval}) -> - State#client_state{stats_timer = emqttd_misc:start_timer(Interval, self(), emit_stats)}. - -stats_by_keepalive(State) -> - State#client_state{enable_stats = keepalive}. - -emit_stats(State = #client_state{enable_stats = false}) -> - State; emit_stats(State = #client_state{proto_state = ProtoState}) -> - {reply, Stats, _} = handle_call(stats, undefined, State), - emqttd_stats:set_client_stats(emqttd_protocol:clientid(ProtoState), Stats), + emit_stats(emqttd_protocol:clientid(ProtoState), State). + +emit_stats(_ClientId, State = #client_state{enable_stats = false}) -> + State; +emit_stats(undefined, State) -> + State; +emit_stats(ClientId, State) -> + {reply, Stats, _, _} = handle_call(stats, undefined, State), + emqttd_stats:set_client_stats(ClientId, Stats), State. sock_stats(#client_state{connection = Conn}) -> - case Conn:getstat(?SOCK_STATS) of - {ok, Ss} -> Ss; - {error, _} -> [] - end. + case Conn:getstat(?SOCK_STATS) of {ok, Ss} -> Ss; {error, _} -> [] end. -hibernate(State) -> - {noreply, State, hibernate}. +reply(Reply, State) -> + {reply, Reply, State, hibernate}. shutdown(Reason, State) -> stop({shutdown, Reason}, State). diff --git a/src/emqttd_misc.erl b/src/emqttd_misc.erl index 8111264cc..69cba9d8f 100644 --- a/src/emqttd_misc.erl +++ b/src/emqttd_misc.erl @@ -19,7 +19,7 @@ -author("Feng Lee "). -export([merge_opts/2, start_timer/2, start_timer/3, cancel_timer/1, - proc_stats/0, proc_stats/1]). + proc_stats/0, proc_stats/1, inc_stats/1]). %% @doc Merge Options merge_opts(Defaults, Options) -> @@ -61,3 +61,5 @@ proc_stats(Pid) -> {value, {_, V}, Stats1} = lists:keytake(message_queue_len, 1, Stats), [{mailbox_len, V} | Stats1]. +inc_stats(Key) -> put(Key, get(Key) + 1). + diff --git a/src/emqttd_protocol.erl b/src/emqttd_protocol.erl index a9cc37778..da235a44e 100644 --- a/src/emqttd_protocol.erl +++ b/src/emqttd_protocol.erl @@ -187,6 +187,8 @@ process(Packet = ?CONNECT_PACKET(Var), State0) -> emqttd_cm:reg(client(State2)), %% Start keepalive start_keepalive(KeepAlive), + %% Emit Stats + self() ! emit_stats, %% ACCEPT {?CONNACK_ACCEPT, SP, State2#proto_state{session = Session, is_superuser = IsSuperuser}}; {error, Error} -> diff --git a/src/emqttd_session.erl b/src/emqttd_session.erl index eb3756460..26d7c7a83 100644 --- a/src/emqttd_session.erl +++ b/src/emqttd_session.erl @@ -74,7 +74,8 @@ terminate/2, code_change/3]). %% gen_server2 Message Priorities --export([prioritise_call/4, prioritise_cast/3, prioritise_info/3]). +-export([prioritise_call/4, prioritise_cast/3, prioritise_info/3, + handle_pre_hibernate/1]). -record(state, { @@ -114,7 +115,7 @@ max_inflight = 32 :: non_neg_integer(), %% Retry interval for redelivering QoS1/2 messages - retry_interval = 20000 :: pos_integer(), + retry_interval = 20000 :: timeout(), %% Retry Timer retry_timer :: reference(), @@ -129,7 +130,7 @@ awaiting_rel :: map(), %% Awaiting PUBREL timeout - await_rel_timeout = 20000 :: pos_integer(), + await_rel_timeout = 20000 :: timeout(), %% Max Packets that Awaiting PUBREL max_awaiting_rel = 100 :: non_neg_integer(), @@ -138,16 +139,13 @@ await_rel_timer :: reference(), %% Session Expiry Interval - expiry_interval = 7200000 :: pos_integer(), + expiry_interval = 7200000 :: timeout(), %% Expired Timer expiry_timer :: reference(), %% Enable Stats - enable_stats :: false | pos_integer(), - - %% Stats Timer - stats_timer :: reference(), + enable_stats :: boolean(), created_at :: erlang:timestamp() }). @@ -301,10 +299,9 @@ init([CleanSess, {ClientId, Username}, ClientPid]) -> expiry_interval = get_value(expiry_interval, Env), enable_stats = EnableStats, created_at = os:timestamp()}, - emqttd_stats:set_session_stats(ClientId, stats(State)), emqttd_sm:register_session(ClientId, CleanSess, info(State)), emqttd_hooks:run('session.created', [ClientId, Username]), - {ok, State, hibernate, {backoff, 1000, 1000, 5000}, ?MODULE}. + {ok, emit_stats(State), hibernate, {backoff, 1000, 1000, 10000}, ?MODULE}. init_stats(Keys) -> lists:foreach(fun(K) -> put(K, 0) end, Keys). @@ -336,10 +333,13 @@ prioritise_info(Msg, _Len, _State) -> _ -> 0 end. -handle_call({publish, Msg = #mqtt_message{qos = ?QOS_2, pktid = PacketId}}, - _From, State = #state{awaiting_rel = AwaitingRel, - await_rel_timer = Timer, - await_rel_timeout = Timeout}) -> +handle_pre_hibernate(State) -> + {hibernate, emit_stats(State)}. + +handle_call({publish, Msg = #mqtt_message{qos = ?QOS_2, pktid = PacketId}}, _From, + State = #state{awaiting_rel = AwaitingRel, + await_rel_timer = Timer, + await_rel_timeout = Timeout}) -> case is_awaiting_full(State) of false -> State1 = case Timer == undefined of @@ -413,51 +413,55 @@ handle_cast({unsubscribe, _From, TopicTable}, %% PUBACK: handle_cast({puback, PacketId}, State = #state{inflight = Inflight}) -> - case Inflight:contain(PacketId) of - true -> - noreply(dequeue(acked(puback, PacketId, State))); - false -> - ?LOG(warning, "The PUBACK ~p is not inflight: ~p", - [PacketId, Inflight:window()], State), - emqttd_metrics:inc('packets/puback/missed'), - noreply(State) - end; + {noreply, + case Inflight:contain(PacketId) of + true -> + dequeue(acked(puback, PacketId, State)); + false -> + ?LOG(warning, "PUBACK ~p missed inflight: ~p", + [PacketId, Inflight:window()], State), + emqttd_metrics:inc('packets/puback/missed'), + State + end, hibernate}; %% PUBREC: handle_cast({pubrec, PacketId}, State = #state{inflight = Inflight}) -> - case Inflight:contain(PacketId) of - true -> - noreply(acked(pubrec, PacketId, State)); - false -> - ?LOG(warning, "The PUBREC ~p is not inflight: ~p", - [PacketId, Inflight:window()], State), - emqttd_metrics:inc('packets/pubrec/missed'), - noreply(State) - end; + {noreply, + case Inflight:contain(PacketId) of + true -> + acked(pubrec, PacketId, State); + false -> + ?LOG(warning, "PUBREC ~p missed inflight: ~p", + [PacketId, Inflight:window()], State), + emqttd_metrics:inc('packets/pubrec/missed'), + State + end, hibernate}; %% PUBREL: handle_cast({pubrel, PacketId}, State = #state{awaiting_rel = AwaitingRel}) -> - case maps:take(PacketId, AwaitingRel) of - {Msg, AwaitingRel1} -> - spawn(emqttd_server, publish, [Msg]),%%:) - noreply(State#state{awaiting_rel = AwaitingRel1}); - error -> - ?LOG(warning, "Cannot find PUBREL: ~p", [PacketId], State), - emqttd_metrics:inc('packets/pubrel/missed'), - noreply(State) - end; + {noreply, + case maps:take(PacketId, AwaitingRel) of + {Msg, AwaitingRel1} -> + spawn(emqttd_server, publish, [Msg]), %%:) + State#state{awaiting_rel = AwaitingRel1}; + error -> + ?LOG(warning, "Cannot find PUBREL: ~p", [PacketId], State), + emqttd_metrics:inc('packets/pubrel/missed'), + State + end, hibernate}; %% PUBCOMP: handle_cast({pubcomp, PacketId}, State = #state{inflight = Inflight}) -> - case Inflight:contain(PacketId) of - true -> - noreply(dequeue(acked(pubcomp, PacketId, State))); - false -> - ?LOG(warning, "The PUBCOMP ~p is not inflight: ~p", - [PacketId, Inflight:window()], State), - emqttd_metrics:inc('packets/pubcomp/missed'), - noreply(State) - end; + {noreply, + case Inflight:contain(PacketId) of + true -> + dequeue(acked(pubcomp, PacketId, State)); + false -> + ?LOG(warning, "The PUBCOMP ~p is not inflight: ~p", + [PacketId, Inflight:window()], State), + emqttd_metrics:inc('packets/pubcomp/missed'), + State + end, hibernate}; %% RESUME: handle_cast({resume, ClientId, ClientPid}, @@ -466,14 +470,13 @@ handle_cast({resume, ClientId, ClientPid}, clean_sess = CleanSess, retry_timer = RetryTimer, await_rel_timer = AwaitTimer, - stats_timer = StatsTimer, expiry_timer = ExpireTimer}) -> ?LOG(info, "Resumed by ~p", [ClientPid], State), %% Cancel Timers lists:foreach(fun emqttd_misc:cancel_timer/1, - [RetryTimer, AwaitTimer, StatsTimer, ExpireTimer]), + [RetryTimer, AwaitTimer, ExpireTimer]), case kick(ClientId, OldClientPid, ClientPid) of ok -> ?LOG(warning, "~p kickout ~p", [ClientPid, OldClientPid], State); @@ -503,13 +506,13 @@ handle_cast({resume, ClientId, ClientPid}, %% Replay delivery and Dequeue pending messages hibernate(emit_stats(dequeue(retry_delivery(true, State1)))); -handle_cast({destroy, ClientId}, State = #state{client_id = ClientId, - client_pid = undefined}) -> +handle_cast({destroy, ClientId}, + State = #state{client_id = ClientId, client_pid = undefined}) -> ?LOG(warning, "Destroyed", [], State), shutdown(destroy, State); -handle_cast({destroy, ClientId}, State = #state{client_id = ClientId, - client_pid = OldClientPid}) -> +handle_cast({destroy, ClientId}, + State = #state{client_id = ClientId, client_pid = OldClientPid}) -> ?LOG(warning, "kickout ~p", [OldClientPid], State), shutdown(conflict, State); @@ -518,27 +521,25 @@ handle_cast(Msg, State) -> %% Dispatch Message handle_info({dispatch, Topic, Msg}, State) when is_record(Msg, mqtt_message) -> - noreply(dispatch(tune_qos(Topic, Msg, State), State)); + {noreply, dispatch(tune_qos(Topic, Msg, State), State), hibernate}; %% Do nothing if the client has been disconnected. handle_info({timeout, _Timer, retry_delivery}, State = #state{client_pid = undefined}) -> hibernate(emit_stats(State#state{retry_timer = undefined})); handle_info({timeout, _Timer, retry_delivery}, State) -> - noreply(emit_stats(retry_delivery(false, State#state{retry_timer = undefined}))); + hibernate(emit_stats(retry_delivery(false, State#state{retry_timer = undefined}))); handle_info({timeout, _Timer, check_awaiting_rel}, State) -> - noreply(expire_awaiting_rel(emit_stats(State#state{await_rel_timer = undefined}))); - -handle_info({timeout, _Timer, emit_stats}, State) -> - hibernate(maybe_enable_stats(emit_stats(State))); + hibernate(expire_awaiting_rel(emit_stats(State#state{await_rel_timer = undefined}))); handle_info({timeout, _Timer, expired}, State) -> ?LOG(info, "Expired, shutdown now.", [], State), shutdown(expired, State); handle_info({'EXIT', ClientPid, _Reason}, - State = #state{clean_sess = true, client_pid = ClientPid}) -> + State = #state{clean_sess = true, + client_pid = ClientPid}) -> {stop, normal, State}; handle_info({'EXIT', ClientPid, Reason}, @@ -548,7 +549,7 @@ handle_info({'EXIT', ClientPid, Reason}, ?LOG(info, "Client ~p EXIT for ~p", [ClientPid, Reason], State), ExpireTimer = start_timer(Interval, expired), State1 = State#state{client_pid = undefined, expiry_timer = ExpireTimer}, - hibernate(maybe_enable_stats(emit_stats(State1))); + hibernate(emit_stats(State1)); handle_info({'EXIT', Pid, _Reason}, State = #state{old_client_pid = Pid}) -> %%ignore @@ -690,7 +691,8 @@ dispatch(Msg = #mqtt_message{qos = QoS}, end. enqueue_msg(Msg, State = #state{mqueue = Q}) -> - inc(enqueue_msg), State#state{mqueue = emqttd_mqueue:in(Msg, Q)}. + emqttd_misc:inc_stats(enqueue_msg), + State#state{mqueue = emqttd_mqueue:in(Msg, Q)}. %%-------------------------------------------------------------------- %% Deliver @@ -700,7 +702,8 @@ redeliver(Msg = #mqtt_message{qos = QoS}, State) -> deliver(Msg#mqtt_message{dup = if QoS =:= ?QOS2 -> false; true -> true end}, State). deliver(Msg, #state{client_pid = Pid}) -> - inc(deliver_msg), Pid ! {deliver, Msg}. + emqttd_misc:inc_stats(deliver_msg), + Pid ! {deliver, Msg}. %%-------------------------------------------------------------------- %% Awaiting ACK for QoS1/QoS2 Messages @@ -785,14 +788,6 @@ next_msg_id(State = #state{next_msg_id = Id}) -> %% Emit session stats %%-------------------------------------------------------------------- -maybe_enable_stats(State = #state{enable_stats = false}) -> - State; -maybe_enable_stats(State = #state{client_pid = Pid}) when is_pid(Pid) -> - State; -maybe_enable_stats(State = #state{enable_stats = Interval}) -> - StatsTimer = start_timer(Interval, emit_stats), - State#state{stats_timer = StatsTimer}. - emit_stats(State = #state{enable_stats = false}) -> State; emit_stats(State = #state{client_id = ClientId}) -> @@ -803,13 +798,8 @@ emit_stats(State = #state{client_id = ClientId}) -> %% Helper functions %%-------------------------------------------------------------------- -inc(Key) -> put(Key, get(Key) + 1). - reply(Reply, State) -> - {reply, Reply, State}. - -noreply(State) -> - {noreply, State}. + {reply, Reply, State, hibernate}. hibernate(State) -> {noreply, State, hibernate}. diff --git a/src/emqttd_ws_client.erl b/src/emqttd_ws_client.erl index 3641a98a8..e2d605bf2 100644 --- a/src/emqttd_ws_client.erl +++ b/src/emqttd_ws_client.erl @@ -16,7 +16,7 @@ -module(emqttd_ws_client). --behaviour(gen_server). +-behaviour(gen_server2). -author("Feng Lee "). @@ -40,9 +40,12 @@ -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). +%% gen_server2 Callbacks +-export([prioritise_call/4, prioritise_info/3, handle_pre_hibernate/1]). + %% WebSocket Client State -record(wsclient_state, {ws_pid, peer, connection, proto_state, keepalive, - enable_stats, stats_timer}). + enable_stats}). -define(SOCK_STATS, [recv_oct, recv_cnt, send_oct, send_cnt, send_pend]). @@ -54,13 +57,13 @@ start_link(Env, WsPid, Req, ReplyChannel) -> gen_server:start_link(?MODULE, [Env, WsPid, Req, ReplyChannel], []). info(CPid) -> - gen_server:call(CPid, info). + gen_server2:call(CPid, info). stats(CPid) -> - gen_server:call(CPid, stats). + gen_server2:call(CPid, stats). kick(CPid) -> - gen_server:call(CPid, kick). + gen_server2:call(CPid, kick). subscribe(CPid, TopicTable) -> CPid ! {subscribe, TopicTable}. @@ -69,7 +72,7 @@ unsubscribe(CPid, Topics) -> CPid ! {unsubscribe, Topics}. session(CPid) -> - gen_server:call(CPid, session). + gen_server2:call(CPid, session). %%-------------------------------------------------------------------- %% gen_server Callbacks @@ -90,28 +93,39 @@ init([Env, WsPid, Req, ReplyChannel]) -> EnableStats = proplists:get_value(client_enable_stats, Env, false), ProtoState = emqttd_protocol:init(Peername, SendFun, [{ws_initial_headers, Headers} | Env]), - {ok, maybe_enable_stats(#wsclient_state{ws_pid = WsPid, - peer = Req:get(peer), - connection = Req:get(connection), - proto_state = ProtoState, - enable_stats = EnableStats}), - proplists:get_value(client_idle_timeout, Env, 30000)}. + IdleTimeout = proplists:get_value(client_idle_timeout, Env, 30000), + {ok, #wsclient_state{ws_pid = WsPid, + peer = Req:get(peer), + connection = Req:get(connection), + proto_state = ProtoState, + enable_stats = EnableStats}, + IdleTimeout, {backoff, 1000, 1000, 5000}, ?MODULE}. + +prioritise_call(Msg, _From, _Len, _State) -> + case Msg of info -> 10; stats -> 10; state -> 10; _ -> 5 end. + +prioritise_info(Msg, _Len, _State) -> + case Msg of {redeliver, _} -> 5; _ -> 0 end. + +handle_pre_hibernate(State = #wsclient_state{peer = Peer}) -> + io:format("WsClient(~s) will hibernate!~n", [Peer]), + {hibernate, emit_stats(State)}. handle_call(info, From, State = #wsclient_state{peer = Peer, proto_state = ProtoState}) -> Info = [{websocket, true}, {peer, Peer} | emqttd_protocol:info(ProtoState)], - {reply, Stats, _} = handle_call(stats, From, State), - {reply, lists:append(Info, Stats), State}; + {reply, Stats, _, _} = handle_call(stats, From, State), + reply(lists:append(Info, Stats), State); handle_call(stats, _From, State = #wsclient_state{proto_state = ProtoState}) -> - {reply, lists:append([emqttd_misc:proc_stats(), - wsock_stats(State), - emqttd_protocol:stats(ProtoState)]), State}; + reply(lists:append([emqttd_misc:proc_stats(), + wsock_stats(State), + emqttd_protocol:stats(ProtoState)]), State); handle_call(kick, _From, State) -> {stop, {shutdown, kick}, ok, State}; handle_call(session, _From, State = #wsclient_state{proto_state = ProtoState}) -> - {reply, emqttd_protocol:session(ProtoState), State}; + reply(emqttd_protocol:session(ProtoState), State); handle_call(Req, _From, State = #wsclient_state{peer = Peer}) -> ?WSLOG(error, Peer, "Unexpected request: ~p", [Req]), @@ -166,8 +180,8 @@ handle_info({redeliver, {?PUBREL, PacketId}}, State) -> emqttd_protocol:pubrel(PacketId, ProtoState) end, State); -handle_info({timeout, _Timer, emit_stats}, State) -> - {noreply, maybe_enable_stats(emit_stats(State)), hibernate}; +handle_info(emit_stats, State) -> + {noreply, emit_stats(State), hibernate}; handle_info(timeout, State) -> shutdown(idle_timeout, State); @@ -185,7 +199,7 @@ handle_info({keepalive, start, Interval}, State = #wsclient_state{peer = Peer, c end end, KeepAlive = emqttd_keepalive:start(StatFun, Interval, {keepalive, check}), - {noreply, stats_by_keepalive(State#wsclient_state{keepalive = KeepAlive})}; + {noreply, State#wsclient_state{keepalive = KeepAlive}, hibernate}; handle_info({keepalive, check}, State = #wsclient_state{peer = Peer, keepalive = KeepAlive}) -> @@ -209,7 +223,7 @@ handle_info({'EXIT', WsPid, Reason}, State = #wsclient_state{peer = Peer, ws_pid handle_info(Info, State = #wsclient_state{peer = Peer}) -> ?WSLOG(error, Peer, "Unexpected Info: ~p", [Info]), - {noreply, State}. + {noreply, State, hibernate}. terminate(Reason, #wsclient_state{proto_state = ProtoState, keepalive = KeepAlive}) -> emqttd_keepalive:cancel(KeepAlive), @@ -227,21 +241,16 @@ code_change(_OldVsn, State, _Extra) -> %% Internal functions %%-------------------------------------------------------------------- -maybe_enable_stats(State = #wsclient_state{enable_stats = false}) -> - State; -maybe_enable_stats(State = #wsclient_state{enable_stats = keepalive}) -> - State; -maybe_enable_stats(State = #wsclient_state{enable_stats = Interval}) -> - State#wsclient_state{stats_timer = emqttd_misc:start_timer(Interval, self(), emit_stats)}. - -stats_by_keepalive(State) -> - State#wsclient_state{enable_stats = keepalive}. - -emit_stats(State = #wsclient_state{enable_stats = false}) -> - State; emit_stats(State = #wsclient_state{proto_state = ProtoState}) -> - {reply, Stats, _} = handle_call(stats, undefined, State), - emqttd_stats:set_client_stats(emqttd_protocol:clientid(ProtoState), Stats), + emit_stats(emqttd_protocol:clientid(ProtoState), State). + +emit_stats(_ClientId, State = #wsclient_state{enable_stats = false}) -> + State; +emit_stats(undefined, State) -> + State; +emit_stats(ClientId, State) -> + {reply, Stats, _, _} = handle_call(stats, undefined, State), + emqttd_stats:set_client_stats(ClientId, Stats), State. wsock_stats(#wsclient_state{connection = Conn}) -> @@ -252,7 +261,10 @@ wsock_stats(#wsclient_state{connection = Conn}) -> with_proto(Fun, State = #wsclient_state{proto_state = ProtoState}) -> {ok, ProtoState1} = Fun(ProtoState), - {noreply, State#wsclient_state{proto_state = ProtoState1}}. + {noreply, State#wsclient_state{proto_state = ProtoState1}, hibernate}. + +reply(Reply, State) -> + {reply, Reply, State, hibernate}. shutdown(Reason, State) -> stop({shutdown, Reason}, State). From ff60578a7da03281b95c95c208abca870171cb4b Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Tue, 21 Feb 2017 18:46:10 +0800 Subject: [PATCH 051/125] Change the datatype of 'enable_stats' to 'flag' --- etc/emq.conf | 8 ++++---- priv/emq.schema | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/etc/emq.conf b/etc/emq.conf index e30ddfa26..0e20dd60d 100644 --- a/etc/emq.conf +++ b/etc/emq.conf @@ -113,8 +113,8 @@ mqtt.max_packet_size = 64KB ## Client Idle Timeout (Second) mqtt.client.idle_timeout = 30s -## Enable client Stats: seconds or off -mqtt.client.enable_stats = off +## Enable client Stats: on | off +mqtt.client.enable_stats = on ##-------------------------------------------------------------------- ## MQTT Session @@ -136,8 +136,8 @@ mqtt.session.max_awaiting_rel = 100 ## Awaiting PUBREL Timeout mqtt.session.await_rel_timeout = 20s -## Enable Statistics at the Interval(seconds) -mqtt.session.enable_stats = off +## Enable Statistics: on | off +mqtt.session.enable_stats = on ## Expired after 1 day: ## w - week diff --git a/priv/emq.schema b/priv/emq.schema index 156bc4874..1e3ba1778 100644 --- a/priv/emq.schema +++ b/priv/emq.schema @@ -329,7 +329,7 @@ end}. %% @doc Enable Stats of Client. {mapping, "mqtt.client.enable_stats", "emqttd.client", [ {default, off}, - {datatype, [{duration, ms}, flag]} + {datatype, flag} ]}. %% @doc Client @@ -375,7 +375,7 @@ end}. %% @doc Enable Stats {mapping, "mqtt.session.enable_stats", "emqttd.session", [ {default, off}, - {datatype, [{duration, ms}, flag]} + {datatype, flag} ]}. %% @doc Session Expiry Interval From d310fff532e41f48eb1ab3c9fbc9cf16b8033aae Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Tue, 21 Feb 2017 18:46:44 +0800 Subject: [PATCH 052/125] Add 'lager_syslog' dependency --- rebar.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rebar.config b/rebar.config index 3ac69aea2..0b43df393 100644 --- a/rebar.config +++ b/rebar.config @@ -1,4 +1,4 @@ {deps, [ -{gproc,".*",{git,"https://github.com/uwiger/gproc",""}},{lager,".*",{git,"https://github.com/basho/lager","master"}},{esockd,".*",{git,"https://github.com/emqtt/esockd","master"}},{mochiweb,".*",{git,"https://github.com/emqtt/mochiweb",""}} +{gproc,".*",{git,"https://github.com/uwiger/gproc",""}},{lager,".*",{git,"https://github.com/basho/lager","master"}},{esockd,".*",{git,"https://github.com/emqtt/esockd","master"}},{mochiweb,".*",{git,"https://github.com/emqtt/mochiweb",""}},{lager_syslog,".*",{git,"https://github.com/basho/lager_syslog",""}} ]}. {erl_opts, [{parse_transform,lager_transform}]}. From 498915e5b3b663a529e742b5ab3612727d656b9d Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Tue, 21 Feb 2017 19:26:50 +0800 Subject: [PATCH 053/125] Hibernate after a packet sent --- src/emqttd_client.erl | 4 ++-- src/emqttd_session.erl | 3 ++- src/emqttd_ws_client.erl | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/emqttd_client.erl b/src/emqttd_client.erl index b48e12f39..765c9d837 100644 --- a/src/emqttd_client.erl +++ b/src/emqttd_client.erl @@ -238,7 +238,7 @@ handle_info({inet_async, _Sock, _Ref, {error, Reason}}, State) -> shutdown(Reason, State); handle_info({inet_reply, _Sock, ok}, State) -> - {noreply, State}; + {noreply, State, hibernate}; handle_info({inet_reply, _Sock, {error, Reason}}, State) -> shutdown(Reason, State); @@ -299,7 +299,7 @@ received(Bytes, State = #client_state{parser_fun = ParserFun, proto_state = ProtoState}) -> case catch ParserFun(Bytes) of {more, NewParser} -> - {noreply, run_socket(State#client_state{parser_fun = NewParser})}; + {noreply, run_socket(State#client_state{parser_fun = NewParser}), hibernate}; {ok, Packet, Rest} -> emqttd_metrics:received(Packet), case emqttd_protocol:received(Packet, ProtoState) of diff --git a/src/emqttd_session.erl b/src/emqttd_session.erl index 26d7c7a83..852f46c5f 100644 --- a/src/emqttd_session.erl +++ b/src/emqttd_session.erl @@ -333,7 +333,8 @@ prioritise_info(Msg, _Len, _State) -> _ -> 0 end. -handle_pre_hibernate(State) -> +handle_pre_hibernate(State = #state{client_id = ClientId}) -> + io:format("Session(~s) will hibernate!~n", [ClientId]), {hibernate, emit_stats(State)}. handle_call({publish, Msg = #mqtt_message{qos = ?QOS_2, pktid = PacketId}}, _From, diff --git a/src/emqttd_ws_client.erl b/src/emqttd_ws_client.erl index e2d605bf2..71cb4b344 100644 --- a/src/emqttd_ws_client.erl +++ b/src/emqttd_ws_client.erl @@ -129,7 +129,7 @@ handle_call(session, _From, State = #wsclient_state{proto_state = ProtoState}) - handle_call(Req, _From, State = #wsclient_state{peer = Peer}) -> ?WSLOG(error, Peer, "Unexpected request: ~p", [Req]), - {reply, {error, unsupported_request}, State}. + reply({error, unsupported_request}, State). handle_cast({received, Packet}, State = #wsclient_state{peer = Peer, proto_state = ProtoState}) -> emqttd_metrics:received(Packet), @@ -147,7 +147,7 @@ handle_cast({received, Packet}, State = #wsclient_state{peer = Peer, proto_state handle_cast(Msg, State = #wsclient_state{peer = Peer}) -> ?WSLOG(error, Peer, "Unexpected msg: ~p", [Msg]), - {noreply, State}. + {noreply, State, hibernate}. handle_info({subscribe, TopicTable}, State) -> with_proto( From de12c58af0872803726e85d03da215c20b402d92 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Tue, 21 Feb 2017 20:09:31 +0800 Subject: [PATCH 054/125] Rename 'MAX_LEN' to 'MAX_PACKET_LEN' --- include/emqttd_protocol.hrl | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/include/emqttd_protocol.hrl b/include/emqttd_protocol.hrl index ab0650ead..a7eb768e3 100644 --- a/include/emqttd_protocol.hrl +++ b/include/emqttd_protocol.hrl @@ -129,11 +129,16 @@ -type(mqtt_connack() :: ?CONNACK_ACCEPT..?CONNACK_AUTH). +%%-------------------------------------------------------------------- +%% Max MQTT Packet Length +%%-------------------------------------------------------------------- + +-define(MAX_PACKET_LEN, 16#fffffff). + %%-------------------------------------------------------------------- %% MQTT Parser and Serializer %%-------------------------------------------------------------------- --define(MAX_LEN, 16#fffffff). -define(HIGHBIT, 2#10000000). -define(LOWBITS, 2#01111111). From 7e9865023375b3546873c144b4f597fb69b2b8c9 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Tue, 21 Feb 2017 20:10:17 +0800 Subject: [PATCH 055/125] Improve the emqttd_parser design --- src/emqttd_client.erl | 27 ++++++++++++++------------- src/emqttd_parser.erl | 29 ++++++++++++----------------- 2 files changed, 26 insertions(+), 30 deletions(-) diff --git a/src/emqttd_client.erl b/src/emqttd_client.erl index 765c9d837..e58f55738 100644 --- a/src/emqttd_client.erl +++ b/src/emqttd_client.erl @@ -52,9 +52,9 @@ -export([prioritise_call/4, prioritise_info/3, handle_pre_hibernate/1]). %% Client State --record(client_state, {connection, connname, peername, peerhost, peerport, - await_recv, conn_state, rate_limit, parser_fun, - proto_state, packet_opts, keepalive, enable_stats}). +-record(client_state, {connection, connname, peername, peerhost, peerport, await_recv, + conn_state, rate_limit, packet_limit, parse_state, proto_state, + keepalive, enable_stats}). -define(INFO_KEYS, [connname, peername, peerhost, peerport, await_recv, conn_state]). @@ -120,9 +120,10 @@ init([Conn0, Env]) -> error:Error -> Self ! {shutdown, Error} end end, - ParserFun = emqttd_parser:new(Env), - ProtoState = emqttd_protocol:init(PeerName, SendFun, Env), RateLimit = get_value(rate_limit, Conn:opts()), + PacketLimit = proplists:get_value(max_packet_size, Env, ?MAX_PACKET_LEN), + ParseState = emqttd_parser:initial_state(PacketLimit), + ProtoState = emqttd_protocol:init(PeerName, SendFun, Env), EnableStats = get_value(client_enable_stats, Env, false), State = run_socket(#client_state{connection = Conn, connname = ConnName, @@ -132,9 +133,9 @@ init([Conn0, Env]) -> await_recv = false, conn_state = running, rate_limit = RateLimit, - parser_fun = ParserFun, + packet_limit = PacketLimit, + parse_state = ParseState, proto_state = ProtoState, - packet_opts = Env, enable_stats = EnableStats}), IdleTimout = get_value(client_idle_timeout, Env, 30000), gen_server2:enter_loop(?MODULE, [], State, self(), IdleTimout, @@ -294,17 +295,17 @@ code_change(_OldVsn, State, _Extra) -> received(<<>>, State) -> {noreply, State, hibernate}; -received(Bytes, State = #client_state{parser_fun = ParserFun, - packet_opts = PacketOpts, +received(Bytes, State = #client_state{parse_state = ParseState, + packet_limit = PacketLimit, proto_state = ProtoState}) -> - case catch ParserFun(Bytes) of - {more, NewParser} -> - {noreply, run_socket(State#client_state{parser_fun = NewParser}), hibernate}; + case catch emqttd_parser:parse(Bytes, ParseState) of + {more, NewParseState} -> + {noreply, run_socket(State#client_state{parse_state = NewParseState}), hibernate}; {ok, Packet, Rest} -> emqttd_metrics:received(Packet), case emqttd_protocol:received(Packet, ProtoState) of {ok, ProtoState1} -> - received(Rest, State#client_state{parser_fun = emqttd_parser:new(PacketOpts), + received(Rest, State#client_state{parse_state = emqttd_parser:initial_state(PacketLimit), proto_state = ProtoState1}); {error, Error} -> ?LOG(error, "Protocol error - ~p", [Error], State), diff --git a/src/emqttd_parser.erl b/src/emqttd_parser.erl index 669f4aab2..bc6e0037b 100644 --- a/src/emqttd_parser.erl +++ b/src/emqttd_parser.erl @@ -24,27 +24,22 @@ -include("emqttd_protocol.hrl"). %% API --export([new/1, parse/2]). +-export([initial_state/0, initial_state/1, parse/2]). --record(mqtt_packet_limit, {max_packet_size}). - --type(option() :: {atom(), any()}). - --type(parser() :: fun( (binary()) -> any() )). +-spec(initial_state() -> {none, pos_integer()}). +initial_state() -> + initial_state(?MAX_PACKET_LEN). %% @doc Initialize a parser --spec(new(Opts :: [option()]) -> parser()). -new(Opts) -> - fun(Bin) -> parse(Bin, {none, limit(Opts)}) end. - -limit(Opts) -> - #mqtt_packet_limit{max_packet_size = proplists:get_value(max_packet_size, Opts, ?MAX_LEN)}. +-spec(initial_state(pos_integer()) -> {none, pos_integer()}). +initial_state(MaxLen) -> + {none, MaxLen}. %% @doc Parse MQTT Packet --spec(parse(binary(), {none, [option()]} | fun()) +-spec(parse(binary(), {none, pos_integer()} | fun()) -> {ok, mqtt_packet()} | {error, any()} | {more, fun()}). -parse(<<>>, {none, Limit}) -> - {more, fun(Bin) -> parse(Bin, {none, Limit}) end}; +parse(<<>>, {none, MaxLen}) -> + {more, fun(Bin) -> parse(Bin, {none, MaxLen}) end}; parse(<>, {none, Limit}) -> parse_remaining_len(Rest, #mqtt_packet_header{type = Type, dup = bool(Dup), @@ -57,7 +52,7 @@ parse_remaining_len(<<>>, Header, Limit) -> parse_remaining_len(Rest, Header, Limit) -> parse_remaining_len(Rest, Header, 1, 0, Limit). -parse_remaining_len(_Bin, _Header, _Multiplier, Length, #mqtt_packet_limit{max_packet_size = MaxLen}) +parse_remaining_len(_Bin, _Header, _Multiplier, Length, MaxLen) when Length > MaxLen -> {error, invalid_mqtt_frame_len}; parse_remaining_len(<<>>, Header, Multiplier, Length, Limit) -> @@ -70,7 +65,7 @@ parse_remaining_len(<<0:8, Rest/binary>>, Header, 1, 0, _Limit) -> parse_frame(Rest, Header, 0); parse_remaining_len(<<1:1, Len:7, Rest/binary>>, Header, Multiplier, Value, Limit) -> parse_remaining_len(Rest, Header, Multiplier * ?HIGHBIT, Value + Len * Multiplier, Limit); -parse_remaining_len(<<0:1, Len:7, Rest/binary>>, Header, Multiplier, Value, #mqtt_packet_limit{max_packet_size = MaxLen}) -> +parse_remaining_len(<<0:1, Len:7, Rest/binary>>, Header, Multiplier, Value, MaxLen) -> FrameLen = Value + Len * Multiplier, if FrameLen > MaxLen -> {error, invalid_mqtt_frame_len}; From 4d2d6fa0d6efbbb9e15e83b6770de3832fbf6f35 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Tue, 21 Feb 2017 20:10:38 +0800 Subject: [PATCH 056/125] Change the default max_len to infinity --- src/emqttd_mqueue.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/emqttd_mqueue.erl b/src/emqttd_mqueue.erl index 9abcc6843..4f825329a 100644 --- a/src/emqttd_mqueue.erl +++ b/src/emqttd_mqueue.erl @@ -76,7 +76,7 @@ %% priority table pseq = 0, priorities = [], %% len of simple queue - len = 0, max_len = ?MAX_LEN, + len = 0, max_len = infinity, low_wm = ?LOW_WM, high_wm = ?HIGH_WM, qos0 = false, dropped = 0, alarm_fun}). From 17953a4716c0aeb60cacd968094f300794422159 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Tue, 21 Feb 2017 20:11:09 +0800 Subject: [PATCH 057/125] Rename 'MAX_LEN' to 'MAX_PACKET_LEN' --- src/emqttd_serializer.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/emqttd_serializer.erl b/src/emqttd_serializer.erl index ed920f584..f6d89cc60 100644 --- a/src/emqttd_serializer.erl +++ b/src/emqttd_serializer.erl @@ -42,7 +42,7 @@ serialize_header(#mqtt_packet_header{type = Type, {VariableBin, PayloadBin}) when ?CONNECT =< Type andalso Type =< ?DISCONNECT -> Len = byte_size(VariableBin) + byte_size(PayloadBin), - true = (Len =< ?MAX_LEN), + true = (Len =< ?MAX_PACKET_LEN), [<>, serialize_len(Len), VariableBin, PayloadBin]. From 62a0eaf65d0d2c7083965d57e8a86240d4afe716 Mon Sep 17 00:00:00 2001 From: Feng Date: Tue, 21 Feb 2017 23:02:10 +0800 Subject: [PATCH 058/125] Fix the test cases for emqttd_parser --- test/emqttd_protocol_SUITE.erl | 56 +++++++++++++++++----------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/test/emqttd_protocol_SUITE.erl b/test/emqttd_protocol_SUITE.erl index 49189bb90..027d550a0 100644 --- a/test/emqttd_protocol_SUITE.erl +++ b/test/emqttd_protocol_SUITE.erl @@ -71,7 +71,7 @@ groups() -> %%-------------------------------------------------------------------- parse_connect(_) -> - Parser = emqttd_parser:new([]), + Parser = emqttd_parser:initial_state(), %% CONNECT(Q0, R0, D0, ClientId=mosqpub/10451-iMac.loca, ProtoName=MQIsdp, ProtoVsn=3, CleanSess=true, KeepAlive=60, Username=undefined, Password=undefined) V31ConnBin = <<16,37,0,6,77,81,73,115,100,112,3,2,0,60,0,23,109,111,115,113,112,117,98,47,49,48,52,53,49,45,105,77,97,99,46,108,111,99,97>>, {ok, #mqtt_packet{header = #mqtt_packet_header{type = ?CONNECT, @@ -82,7 +82,7 @@ parse_connect(_) -> proto_name = <<"MQIsdp">>, client_id = <<"mosqpub/10451-iMac.loca">>, clean_sess = true, - keep_alive = 60}}, <<>>} = Parser(V31ConnBin), + keep_alive = 60}}, <<>>} = emqttd_parser:parse(V31ConnBin, Parser), %% CONNECT(Q0, R0, D0, ClientId=mosqpub/10451-iMac.loca, ProtoName=MQTT, ProtoVsn=4, CleanSess=true, KeepAlive=60, Username=undefined, Password=undefined) V311ConnBin = <<16,35,0,4,77,81,84,84,4,2,0,60,0,23,109,111,115,113,112,117,98,47,49,48,52,53,49,45,105,77,97,99,46,108,111,99,97>>, {ok, #mqtt_packet{header = #mqtt_packet_header{type = ?CONNECT, @@ -93,7 +93,7 @@ parse_connect(_) -> proto_name = <<"MQTT">>, client_id = <<"mosqpub/10451-iMac.loca">>, clean_sess = true, - keep_alive = 60 } }, <<>>} = Parser(V311ConnBin), + keep_alive = 60 } }, <<>>} = emqttd_parser:parse(V311ConnBin, Parser), %% CONNECT(Qos=0, Retain=false, Dup=false, ClientId="", ProtoName=MQTT, ProtoVsn=4, CleanSess=true, KeepAlive=60) V311ConnWithoutClientId = <<16,12,0,4,77,81,84,84,4,2,0,60,0,0>>, @@ -105,7 +105,7 @@ parse_connect(_) -> proto_name = <<"MQTT">>, client_id = <<>>, clean_sess = true, - keep_alive = 60 } }, <<>>} = Parser(V311ConnWithoutClientId), + keep_alive = 60 } }, <<>>} = emqttd_parser:parse(V311ConnWithoutClientId, Parser), %%CONNECT(Q0, R0, D0, ClientId=mosqpub/10452-iMac.loca, ProtoName=MQIsdp, ProtoVsn=3, CleanSess=true, KeepAlive=60, %% Username=test, Password=******, Will(Qos=1, Retain=false, Topic=/will, Msg=willmsg)) ConnBinWithWill = <<16,67,0,6,77,81,73,115,100,112,3,206,0,60,0,23,109,111,115,113,112,117,98,47,49,48,52,53,50,45,105,77,97,99,46,108,111,99,97,0,5,47,119,105,108,108,0,7,119,105,108,108,109,115,103,0,4,116,101,115,116,0,6,112,117,98,108,105,99>>, @@ -124,18 +124,18 @@ parse_connect(_) -> will_topic = <<"/will">>, will_msg = <<"willmsg">>, username = <<"test">>, - password = <<"public">>}}, <<>>} = Parser(ConnBinWithWill), + password = <<"public">>}}, <<>>} = emqttd_parser:parse(ConnBinWithWill, Parser), ok. parse_bridge(_) -> - Parser = emqttd_parser:new([]), + Parser = emqttd_parser:initial_state(), Data = <<16,86,0,6,77,81,73,115,100,112,131,44,0,60,0,19,67,95,48,48,58,48,67,58,50,57,58,50,66,58,55,55,58,53,50, 0,48,36,83,89,83,47,98,114,111,107,101,114,47,99,111,110,110,101,99,116,105,111,110,47,67,95,48,48,58,48, 67,58,50,57,58,50,66,58,55,55,58,53,50,47,115,116,97,116,101,0,1,48>>, %% CONNECT(Q0, R0, D0, ClientId=C_00:0C:29:2B:77:52, ProtoName=MQIsdp, ProtoVsn=131, CleanSess=false, KeepAlive=60, %% Username=undefined, Password=undefined, Will(Q1, R1, Topic=$SYS/broker/connection/C_00:0C:29:2B:77:52/state, Msg=0)) - {ok, #mqtt_packet{variable = Variable}, <<>>} = Parser(Data), + {ok, #mqtt_packet{variable = Variable}, <<>>} = emqttd_parser:parse(Data, Parser), #mqtt_packet_connect{client_id = <<"C_00:0C:29:2B:77:52">>, proto_ver = 16#03, proto_name = <<"MQIsdp">>, @@ -148,7 +148,7 @@ parse_bridge(_) -> will_msg = <<"0">>} = Variable. parse_publish(_) -> - Parser = emqttd_parser:new([]), + Parser = emqttd_parser:initial_state(), %%PUBLISH(Qos=1, Retain=false, Dup=false, TopicName=a/b/c, PacketId=1, Payload=<<"hahah">>) PubBin = <<50,14,0,5,97,47,98,47,99,0,1,104,97,104,97,104>>, {ok, #mqtt_packet{header = #mqtt_packet_header{type = ?PUBLISH, @@ -157,7 +157,7 @@ parse_publish(_) -> retain = false}, variable = #mqtt_packet_publish{topic_name = <<"a/b/c">>, packet_id = 1}, - payload = <<"hahah">> }, <<>>} = Parser(PubBin), + payload = <<"hahah">> }, <<>>} = emqttd_parser:parse(PubBin, Parser), %PUBLISH(Qos=0, Retain=false, Dup=false, TopicName=xxx/yyy, PacketId=undefined, Payload=<<"hello">>) %DISCONNECT(Qos=0, Retain=false, Dup=false) @@ -168,43 +168,43 @@ parse_publish(_) -> retain = false}, variable = #mqtt_packet_publish{topic_name = <<"xxx/yyy">>, packet_id = undefined}, - payload = <<"hello">> }, <<224,0>>} = Parser(PubBin1), + payload = <<"hello">> }, <<224,0>>} = emqttd_parser:parse(PubBin1, Parser), {ok, #mqtt_packet{header = #mqtt_packet_header{type = ?DISCONNECT, dup = false, qos = 0, - retain = false}}, <<>>} = Parser(<<224, 0>>). + retain = false}}, <<>>} = emqttd_parser:parse(<<224, 0>>, Parser). parse_puback(_) -> - Parser = emqttd_parser:new([]), + Parser = emqttd_parser:initial_state(), %%PUBACK(Qos=0, Retain=false, Dup=false, PacketId=1) {ok, #mqtt_packet{header = #mqtt_packet_header{type = ?PUBACK, dup = false, qos = 0, - retain = false}}, <<>>} = Parser(<<64,2,0,1>>). + retain = false}}, <<>>} = emqttd_parser:parse(<<64,2,0,1>>, Parser). parse_pubrec(_) -> - Parser = emqttd_parser:new([]), + Parser = emqttd_parser:initial_state(), %%PUBREC(Qos=0, Retain=false, Dup=false, PacketId=1) {ok, #mqtt_packet{header = #mqtt_packet_header{type = ?PUBREC, dup = false, qos = 0, - retain = false}}, <<>>} = Parser(<<5:4,0:4,2,0,1>>). + retain = false}}, <<>>} = emqttd_parser:parse(<<5:4,0:4,2,0,1>>, Parser). parse_pubrel(_) -> - Parser = emqttd_parser:new([]), + Parser = emqttd_parser:initial_state(), {ok, #mqtt_packet{header = #mqtt_packet_header{type = ?PUBREL, dup = false, qos = 1, - retain = false}}, <<>>} = Parser(<<6:4,2:4,2,0,1>>). + retain = false}}, <<>>} = emqttd_parser:parse(<<6:4,2:4,2,0,1>>, Parser). parse_pubcomp(_) -> - Parser = emqttd_parser:new([]), + Parser = emqttd_parser:initial_state(), {ok, #mqtt_packet{header = #mqtt_packet_header{type = ?PUBCOMP, dup = false, qos = 0, - retain = false}}, <<>>} = Parser(<<7:4,0:4,2,0,1>>). + retain = false}}, <<>>} = emqttd_parser:parse(<<7:4,0:4,2,0,1>>, Parser). parse_subscribe(_) -> - Parser = emqttd_parser:new([]), + Parser = emqttd_parser:initial_state(), %% SUBSCRIBE(Q1, R0, D0, PacketId=2, TopicTable=[{<<"TopicA">>,2}]) {ok, #mqtt_packet{header = #mqtt_packet_header{type = ?SUBSCRIBE, dup = false, @@ -212,10 +212,10 @@ parse_subscribe(_) -> retain = false}, variable = #mqtt_packet_subscribe{packet_id = 2, topic_table = [{<<"TopicA">>,2}]} }, <<>>} - = Parser(<<130,11,0,2,0,6,84,111,112,105,99,65,2>>). + = emqttd_parser:parse(<<130,11,0,2,0,6,84,111,112,105,99,65,2>>, Parser). parse_unsubscribe(_) -> - Parser = emqttd_parser:new([]), + Parser = emqttd_parser:initial_state(), %% UNSUBSCRIBE(Q1, R0, D0, PacketId=2, TopicTable=[<<"TopicA">>]) {ok, #mqtt_packet{header = #mqtt_packet_header{type = ?UNSUBSCRIBE, dup = false, @@ -223,24 +223,24 @@ parse_unsubscribe(_) -> retain = false}, variable = #mqtt_packet_unsubscribe{packet_id = 2, topics = [<<"TopicA">>]}}, <<>>} - = Parser(<<162,10,0,2,0,6,84,111,112,105,99,65>>). + = emqttd_parser:parse(<<162,10,0,2,0,6,84,111,112,105,99,65>>, Parser). parse_pingreq(_) -> - Parser = emqttd_parser:new([]), + Parser = emqttd_parser:initial_state(), {ok, #mqtt_packet{header = #mqtt_packet_header{type = ?PINGREQ, dup = false, qos = 0, retain = false}}, <<>>} - = Parser(<>). + = emqttd_parser:parse(<>, Parser). parse_disconnect(_) -> - Parser = emqttd_parser:new([]), + Parser = emqttd_parser:initial_state(), %DISCONNECT(Qos=0, Retain=false, Dup=false) Bin = <<224, 0>>, {ok, #mqtt_packet{header = #mqtt_packet_header{type = ?DISCONNECT, dup = false, qos = 0, - retain = false}}, <<>>} = Parser(Bin). + retain = false}}, <<>>} = emqttd_parser:parse(Bin, Parser). %%-------------------------------------------------------------------- %% Serialize Cases @@ -260,7 +260,7 @@ serialize_connect(_) -> serialize_connack(_) -> ConnAck = #mqtt_packet{header = #mqtt_packet_header{type = ?CONNACK}, variable = #mqtt_packet_connack{ack_flags = 0, return_code = 0}}, - <<32,2,0,0>> = serialize(ConnAck). + <<32,2,0,0>> = iolist_to_binary(serialize(ConnAck)). serialize_publish(_) -> serialize(?PUBLISH_PACKET(?QOS_0, <<"Topic">>, undefined, <<"Payload">>)), From d33a41b28bf0b42417bb3b3813e9f8f0178f7016 Mon Sep 17 00:00:00 2001 From: Feng Date: Wed, 22 Feb 2017 00:31:09 +0800 Subject: [PATCH 059/125] Remove unused fields: connname, peerhost, peerport --- src/emqttd_client.erl | 93 +++++++++++++++++++++---------------------- 1 file changed, 46 insertions(+), 47 deletions(-) diff --git a/src/emqttd_client.erl b/src/emqttd_client.erl index e58f55738..c347d73f7 100644 --- a/src/emqttd_client.erl +++ b/src/emqttd_client.erl @@ -52,16 +52,18 @@ -export([prioritise_call/4, prioritise_info/3, handle_pre_hibernate/1]). %% Client State --record(client_state, {connection, connname, peername, peerhost, peerport, await_recv, - conn_state, rate_limit, packet_limit, parse_state, proto_state, +%% Unused fields: connname, peerhost, peerport +-record(client_state, {connection, peername, conn_state, await_recv, + rate_limit, packet_size, parser, proto_state, keepalive, enable_stats}). --define(INFO_KEYS, [connname, peername, peerhost, peerport, await_recv, conn_state]). +-define(INFO_KEYS, [peername, conn_state, await_recv]). -define(SOCK_STATS, [recv_oct, recv_cnt, send_oct, send_cnt, send_pend]). -define(LOG(Level, Format, Args, State), - lager:Level("Client(~s): " ++ Format, [State#client_state.connname | Args])). + lager:Level("Client(~s): " ++ Format, + [esockd_net:format(State#client_state.peername) | Args])). start_link(Conn, Env) -> {ok, proc_lib:spawn_link(?MODULE, init, [[Conn, Env]])}. @@ -96,50 +98,47 @@ session(CPid) -> init([Conn0, Env]) -> {ok, Conn} = Conn0:wait(), - {PeerHost, PeerPort, PeerName} = case Conn:peername() of - {ok, Peer = {Host, Port}} -> - {Host, Port, Peer}; - {error, enotconn} -> - Conn:fast_close(), - exit(normal); - {error, Reason} -> - Conn:fast_close(), - exit({shutdown, Reason}) - end, - ConnName = esockd_net:format(PeerName), + {ok, Peername} -> do_init(Conn, Env, Peername); + {error, enotconn} -> Conn:fast_close(), + exit(normal); + {error, Reason} -> Conn:fast_close(), + exit({shutdown, Reason}) + end. + +do_init(Conn, Env, Peername) -> + %% Send Fun + SendFun = send_fun(Conn, Peername), + RateLimit = get_value(rate_limit, Conn:opts()), + PacketSize = get_value(max_packet_size, Env, ?MAX_PACKET_SIZE), + Parser = emqttd_parser:initial_state(PacketSize), + ProtoState = emqttd_protocol:init(Peername, SendFun, Env), + EnableStats = get_value(client_enable_stats, Env, false), + State = run_socket(#client_state{connection = Conn, + peername = Peername, + await_recv = false, + conn_state = running, + rate_limit = RateLimit, + packet_size = PacketSize, + parser = Parser, + proto_state = ProtoState, + enable_stats = EnableStats}), + IdleTimout = get_value(client_idle_timeout, Env, 30000), + gen_server2:enter_loop(?MODULE, [], State, self(), IdleTimout, + {backoff, 1000, 1000, 5000}). + +send_fun(Conn, Peername) -> Self = self(), - %% Send Packet... - SendFun = fun(Packet) -> + fun(Packet) -> Data = emqttd_serializer:serialize(Packet), - ?LOG(debug, "SEND ~p", [Data], #client_state{connname = ConnName}), + ?LOG(debug, "SEND ~p", [Data], #client_state{peername = Peername}), emqttd_metrics:inc('bytes/sent', iolist_size(Data)), try Conn:async_send(Data) of true -> ok catch error:Error -> Self ! {shutdown, Error} end - end, - RateLimit = get_value(rate_limit, Conn:opts()), - PacketLimit = proplists:get_value(max_packet_size, Env, ?MAX_PACKET_LEN), - ParseState = emqttd_parser:initial_state(PacketLimit), - ProtoState = emqttd_protocol:init(PeerName, SendFun, Env), - EnableStats = get_value(client_enable_stats, Env, false), - State = run_socket(#client_state{connection = Conn, - connname = ConnName, - peername = PeerName, - peerhost = PeerHost, - peerport = PeerPort, - await_recv = false, - conn_state = running, - rate_limit = RateLimit, - packet_limit = PacketLimit, - parse_state = ParseState, - proto_state = ProtoState, - enable_stats = EnableStats}), - IdleTimout = get_value(client_idle_timeout, Env, 30000), - gen_server2:enter_loop(?MODULE, [], State, self(), IdleTimout, - {backoff, 1000, 1000, 5000}). + end. prioritise_call(Msg, _From, _Len, _State) -> case Msg of info -> 10; stats -> 10; state -> 10; _ -> 5 end. @@ -147,8 +146,8 @@ prioritise_call(Msg, _From, _Len, _State) -> prioritise_info(Msg, _Len, _State) -> case Msg of {redeliver, _} -> 5; _ -> 0 end. -handle_pre_hibernate(State = #client_state{connname = Connname}) -> - io:format("Client(~s) will hibernate!~n", [Connname]), +handle_pre_hibernate(State = #client_state{peername = Peername}) -> + io:format("Client(~s) will hibernate!~n", [esockd_net:format(Peername)]), {hibernate, emit_stats(State)}. handle_call(info, From, State = #client_state{proto_state = ProtoState}) -> @@ -295,17 +294,17 @@ code_change(_OldVsn, State, _Extra) -> received(<<>>, State) -> {noreply, State, hibernate}; -received(Bytes, State = #client_state{parse_state = ParseState, - packet_limit = PacketLimit, +received(Bytes, State = #client_state{parser = Parser, + packet_size = PacketSize, proto_state = ProtoState}) -> - case catch emqttd_parser:parse(Bytes, ParseState) of - {more, NewParseState} -> - {noreply, run_socket(State#client_state{parse_state = NewParseState}), hibernate}; + case catch emqttd_parser:parse(Bytes, Parser) of + {more, NewParser} -> + {noreply, run_socket(State#client_state{parser = NewParser}), hibernate}; {ok, Packet, Rest} -> emqttd_metrics:received(Packet), case emqttd_protocol:received(Packet, ProtoState) of {ok, ProtoState1} -> - received(Rest, State#client_state{parse_state = emqttd_parser:initial_state(PacketLimit), + received(Rest, State#client_state{parser = emqttd_parser:initial_state(PacketSize), proto_state = ProtoState1}); {error, Error} -> ?LOG(error, "Protocol error - ~p", [Error], State), From 418fb37ca0f32dc179c8ff4fea010edea8bba497 Mon Sep 17 00:00:00 2001 From: Feng Date: Wed, 22 Feb 2017 00:31:47 +0800 Subject: [PATCH 060/125] Add max_packet_size() type --- src/emqttd_parser.erl | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/emqttd_parser.erl b/src/emqttd_parser.erl index bc6e0037b..dde9ae4dc 100644 --- a/src/emqttd_parser.erl +++ b/src/emqttd_parser.erl @@ -26,14 +26,16 @@ %% API -export([initial_state/0, initial_state/1, parse/2]). --spec(initial_state() -> {none, pos_integer()}). +-type(max_packet_size() :: 1..?MAX_PACKET_SIZE). + +-spec(initial_state() -> {none, max_packet_size()}). initial_state() -> - initial_state(?MAX_PACKET_LEN). + initial_state(?MAX_PACKET_SIZE). %% @doc Initialize a parser --spec(initial_state(pos_integer()) -> {none, pos_integer()}). -initial_state(MaxLen) -> - {none, MaxLen}. +-spec(initial_state(max_packet_size()) -> {none, max_packet_size()}). +initial_state(MaxSize) -> + {none, MaxSize}. %% @doc Parse MQTT Packet -spec(parse(binary(), {none, pos_integer()} | fun()) From 6c50a59cadc949bb4ed1708fb8efaece4c1d4d19 Mon Sep 17 00:00:00 2001 From: Feng Date: Wed, 22 Feb 2017 00:32:22 +0800 Subject: [PATCH 061/125] Rename macro 'MAX_PACKET_LEN' to 'MAX_PACKET_SIZE' --- include/emqttd_protocol.hrl | 2 +- src/emqttd_serializer.erl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/emqttd_protocol.hrl b/include/emqttd_protocol.hrl index a7eb768e3..6181b9a65 100644 --- a/include/emqttd_protocol.hrl +++ b/include/emqttd_protocol.hrl @@ -133,7 +133,7 @@ %% Max MQTT Packet Length %%-------------------------------------------------------------------- --define(MAX_PACKET_LEN, 16#fffffff). +-define(MAX_PACKET_SIZE, 16#fffffff). %%-------------------------------------------------------------------- %% MQTT Parser and Serializer diff --git a/src/emqttd_serializer.erl b/src/emqttd_serializer.erl index f6d89cc60..a47a23c8b 100644 --- a/src/emqttd_serializer.erl +++ b/src/emqttd_serializer.erl @@ -42,7 +42,7 @@ serialize_header(#mqtt_packet_header{type = Type, {VariableBin, PayloadBin}) when ?CONNECT =< Type andalso Type =< ?DISCONNECT -> Len = byte_size(VariableBin) + byte_size(PayloadBin), - true = (Len =< ?MAX_PACKET_LEN), + true = (Len =< ?MAX_PACKET_SIZE), [<>, serialize_len(Len), VariableBin, PayloadBin]. From 700ec7aaefe6e63c2254fbd186cdf8c060f75b2b Mon Sep 17 00:00:00 2001 From: Feng Date: Wed, 22 Feb 2017 10:01:39 +0800 Subject: [PATCH 062/125] Add 'proto_stats' record --- src/emqttd_protocol.erl | 54 ++++++++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 20 deletions(-) diff --git a/src/emqttd_protocol.erl b/src/emqttd_protocol.erl index da235a44e..7ee9a7a6a 100644 --- a/src/emqttd_protocol.erl +++ b/src/emqttd_protocol.erl @@ -35,12 +35,14 @@ -export([process/2]). +-record(proto_stats, {recv_pkt = 0, recv_msg = 0, send_pkt = 0, send_msg = 0}). + %% Protocol State -record(proto_state, {peername, sendfun, connected = false, client_id, client_pid, clean_sess, proto_ver, proto_name, username, is_superuser = false, will_msg, keepalive, max_clientid_len = ?MAX_CLIENTID_LEN, - session, ws_initial_headers, %% Headers from first HTTP request for websocket client + session, stats, ws_initial_headers, %% Headers from first HTTP request for websocket client connected_at}). -type(proto_state() :: #proto_state{}). @@ -56,20 +58,20 @@ %% @doc Init protocol init(Peername, SendFun, Opts) -> - lists:foreach(fun(K) -> put(K, 0) end, ?STATS_KEYS), MaxLen = get_value(max_clientid_len, Opts, ?MAX_CLIENTID_LEN), WsInitialHeaders = get_value(ws_initial_headers, Opts), #proto_state{peername = Peername, sendfun = SendFun, max_clientid_len = MaxLen, client_pid = self(), + stats = #proto_stats{}, ws_initial_headers = WsInitialHeaders}. info(ProtoState) -> ?record_to_proplist(proto_state, ProtoState, ?INFO_KEYS). -stats(_ProtoState) -> - [{K, get(K)} || K <- ?STATS_KEYS]. +stats(#proto_state{stats = Stats}) -> + ?record_to_proplist(proto_stats, Stats). clientid(#proto_state{client_id = ClientId}) -> ClientId. @@ -106,8 +108,10 @@ session(#proto_state{session = Session}) -> %% A Client can only send the CONNECT Packet once over a Network Connection. -spec(received(mqtt_packet(), proto_state()) -> {ok, proto_state()} | {error, any()}). -received(Packet = ?PACKET(?CONNECT), State = #proto_state{connected = false}) -> - process(Packet, State#proto_state{connected = true}); +received(Packet = ?PACKET(?CONNECT), + State = #proto_state{connected = false, stats = Stats}) -> + trace(recv, Packet, State), Stats1 = inc_stats(recv, ?CONNECT, Stats), + process(Packet, State#proto_state{connected = true, stats = Stats1}); received(?PACKET(?CONNECT), State = #proto_state{connected = true}) -> {error, protocol_bad_connect, State}; @@ -116,11 +120,11 @@ received(?PACKET(?CONNECT), State = #proto_state{connected = true}) -> received(_Packet, State = #proto_state{connected = false}) -> {error, protocol_not_connected, State}; -received(Packet = ?PACKET(_Type), State) -> - trace(recv, Packet, State), +received(Packet = ?PACKET(Type), State = #proto_state{stats = Stats}) -> + trace(recv, Packet, State), Stats1 = inc_stats(recv, Type, Stats), case validate_packet(Packet) of ok -> - process(Packet, State); + process(Packet, State#proto_state{stats = Stats1}); {error, Reason} -> {error, Reason, State} end. @@ -151,7 +155,7 @@ unsubscribe(RawTopics, ProtoState = #proto_state{client_id = ClientId, %% @doc Send PUBREL pubrel(PacketId, State) -> send(?PUBREL_PACKET(PacketId), State). -process(Packet = ?CONNECT_PACKET(Var), State0) -> +process(?CONNECT_PACKET(Var), State0) -> #mqtt_packet_connect{proto_ver = ProtoVer, proto_name = ProtoName, @@ -170,8 +174,6 @@ process(Packet = ?CONNECT_PACKET(Var), State0) -> will_msg = willmsg(Var), connected_at = os:timestamp()}, - trace(recv, Packet, State1), - {ReturnCode1, SessPresent, State3} = case validate_connect(Var, State1) of ?CONNACK_ACCEPT -> @@ -312,22 +314,34 @@ send(Msg, State = #proto_state{client_id = ClientId, username = Username}) emqttd_hooks:run('message.delivered', [ClientId, Username], Msg), send(emqttd_message:to_packet(Msg), State); -send(Packet, State = #proto_state{sendfun = SendFun}) - when is_record(Packet, mqtt_packet) -> +send(Packet = ?PACKET(Type), + State = #proto_state{sendfun = SendFun, stats = Stats}) -> trace(send, Packet, State), emqttd_metrics:sent(Packet), SendFun(Packet), - {ok, State}. + Stats1 = inc_stats(send, Type, Stats), + {ok, State#proto_state{stats = Stats1}}. -trace(recv, Packet = ?PACKET(Type), ProtoState) -> - inc(recv_pkt), ?IF(Type =:= ?PUBLISH, inc(recv_msg), ok), +trace(recv, Packet, ProtoState) -> ?LOG(info, "RECV ~s", [emqttd_packet:format(Packet)], ProtoState); -trace(send, Packet = ?PACKET(Type), ProtoState) -> - inc(send_pkt), ?IF(Type =:= ?PUBLISH, inc(send_msg), ok), +trace(send, Packet, ProtoState) -> ?LOG(info, "SEND ~s", [emqttd_packet:format(Packet)], ProtoState). -inc(Key) -> put(Key, get(Key) + 1). +inc_stats(recv, Type, Stats) -> + #proto_stats{recv_pkt = Pkt, recv_msg = Msg} = Stats, + inc_stats(Type, #proto_stats.recv_pkt, Pkt, #proto_stats.recv_msg, Msg, Stats); + +inc_stats(send, Type, Stats) -> + #proto_stats{send_pkt = Pkt, send_msg = Msg} = Stats, + inc_stats(Type, #proto_stats.send_pkt, Pkt, #proto_stats.send_msg, Msg, Stats). + +inc_stats(Type, PktPos, PktCnt, MsgPos, MsgCnt, Stats) -> + Stats1 = setelement(PktPos, Stats, PktCnt + 1), + case Type =:= ?PUBLISH of + true -> setelement(MsgPos, Stats1, MsgCnt + 1); + false -> Stats1 + end. stop_if_auth_failure(RC, State) when RC == ?CONNACK_CREDENTIALS; RC == ?CONNACK_AUTH -> {stop, {shutdown, auth_failure}, State}; From f4c4e5635c31c5c017ac8d3c1ee37f98cc8856b5 Mon Sep 17 00:00:00 2001 From: Feng Date: Wed, 22 Feb 2017 12:10:52 +0800 Subject: [PATCH 063/125] Improve the 'enable_stats' design of client, session --- src/emqttd_client.erl | 6 +-- src/emqttd_protocol.erl | 41 +++++++++-------- src/emqttd_ws_client.erl | 95 +++++++++++++++++++++------------------- 3 files changed, 77 insertions(+), 65 deletions(-) diff --git a/src/emqttd_client.erl b/src/emqttd_client.erl index c347d73f7..93efa938b 100644 --- a/src/emqttd_client.erl +++ b/src/emqttd_client.erl @@ -14,7 +14,7 @@ %% limitations under the License. %%-------------------------------------------------------------------- -%% @doc MQTT/TCP Connection +%% @doc MQTT/TCP Connection. -module(emqttd_client). @@ -57,7 +57,7 @@ rate_limit, packet_size, parser, proto_state, keepalive, enable_stats}). --define(INFO_KEYS, [peername, conn_state, await_recv]). +-define(INFO_KEYS, [peername, conn_state, await_recv, enable_stats]). -define(SOCK_STATS, [recv_oct, recv_cnt, send_oct, send_cnt, send_pend]). @@ -125,7 +125,7 @@ do_init(Conn, Env, Peername) -> enable_stats = EnableStats}), IdleTimout = get_value(client_idle_timeout, Env, 30000), gen_server2:enter_loop(?MODULE, [], State, self(), IdleTimout, - {backoff, 1000, 1000, 5000}). + {backoff, 1000, 1000, 10000}). send_fun(Conn, Peername) -> Self = self(), diff --git a/src/emqttd_protocol.erl b/src/emqttd_protocol.erl index 7ee9a7a6a..4b3c3ca76 100644 --- a/src/emqttd_protocol.erl +++ b/src/emqttd_protocol.erl @@ -35,15 +35,15 @@ -export([process/2]). --record(proto_stats, {recv_pkt = 0, recv_msg = 0, send_pkt = 0, send_msg = 0}). +-record(proto_stats, {enable_stats = false, recv_pkt = 0, recv_msg = 0, + send_pkt = 0, send_msg = 0}). %% Protocol State --record(proto_state, {peername, sendfun, connected = false, - client_id, client_pid, clean_sess, - proto_ver, proto_name, username, is_superuser = false, - will_msg, keepalive, max_clientid_len = ?MAX_CLIENTID_LEN, - session, stats, ws_initial_headers, %% Headers from first HTTP request for websocket client - connected_at}). +%% ws_initial_headers: Headers from first HTTP request for WebSocket Client. +-record(proto_state, {peername, sendfun, connected = false, client_id, client_pid, + clean_sess, proto_ver, proto_name, username, is_superuser, + will_msg, keepalive, max_clientid_len, session, stats_data, + ws_initial_headers, connected_at}). -type(proto_state() :: #proto_state{}). @@ -58,20 +58,22 @@ %% @doc Init protocol init(Peername, SendFun, Opts) -> + EnableStats = get_value(client_enable_stats, Opts, false), MaxLen = get_value(max_clientid_len, Opts, ?MAX_CLIENTID_LEN), WsInitialHeaders = get_value(ws_initial_headers, Opts), #proto_state{peername = Peername, sendfun = SendFun, - max_clientid_len = MaxLen, client_pid = self(), - stats = #proto_stats{}, - ws_initial_headers = WsInitialHeaders}. + max_clientid_len = MaxLen, + is_superuser = false, + ws_initial_headers = WsInitialHeaders, + stats_data = #proto_stats{enable_stats = EnableStats}}. info(ProtoState) -> ?record_to_proplist(proto_state, ProtoState, ?INFO_KEYS). -stats(#proto_state{stats = Stats}) -> - ?record_to_proplist(proto_stats, Stats). +stats(#proto_state{stats_data = Stats}) -> + tl(?record_to_proplist(proto_stats, Stats)). clientid(#proto_state{client_id = ClientId}) -> ClientId. @@ -109,9 +111,9 @@ session(#proto_state{session = Session}) -> %% A Client can only send the CONNECT Packet once over a Network Connection. -spec(received(mqtt_packet(), proto_state()) -> {ok, proto_state()} | {error, any()}). received(Packet = ?PACKET(?CONNECT), - State = #proto_state{connected = false, stats = Stats}) -> + State = #proto_state{connected = false, stats_data = Stats}) -> trace(recv, Packet, State), Stats1 = inc_stats(recv, ?CONNECT, Stats), - process(Packet, State#proto_state{connected = true, stats = Stats1}); + process(Packet, State#proto_state{connected = true, stats_data = Stats1}); received(?PACKET(?CONNECT), State = #proto_state{connected = true}) -> {error, protocol_bad_connect, State}; @@ -120,11 +122,11 @@ received(?PACKET(?CONNECT), State = #proto_state{connected = true}) -> received(_Packet, State = #proto_state{connected = false}) -> {error, protocol_not_connected, State}; -received(Packet = ?PACKET(Type), State = #proto_state{stats = Stats}) -> +received(Packet = ?PACKET(Type), State = #proto_state{stats_data = Stats}) -> trace(recv, Packet, State), Stats1 = inc_stats(recv, Type, Stats), case validate_packet(Packet) of ok -> - process(Packet, State#proto_state{stats = Stats1}); + process(Packet, State#proto_state{stats_data = Stats1}); {error, Reason} -> {error, Reason, State} end. @@ -315,12 +317,12 @@ send(Msg, State = #proto_state{client_id = ClientId, username = Username}) send(emqttd_message:to_packet(Msg), State); send(Packet = ?PACKET(Type), - State = #proto_state{sendfun = SendFun, stats = Stats}) -> + State = #proto_state{sendfun = SendFun, stats_data = Stats}) -> trace(send, Packet, State), emqttd_metrics:sent(Packet), SendFun(Packet), Stats1 = inc_stats(send, Type, Stats), - {ok, State#proto_state{stats = Stats1}}. + {ok, State#proto_state{stats_data = Stats1}}. trace(recv, Packet, ProtoState) -> ?LOG(info, "RECV ~s", [emqttd_packet:format(Packet)], ProtoState); @@ -328,6 +330,9 @@ trace(recv, Packet, ProtoState) -> trace(send, Packet, ProtoState) -> ?LOG(info, "SEND ~s", [emqttd_packet:format(Packet)], ProtoState). +inc_stats(_Direct, _Type, Stats = #proto_stats{enable_stats = false}) -> + Stats; + inc_stats(recv, Type, Stats) -> #proto_stats{recv_pkt = Pkt, recv_msg = Msg} = Stats, inc_stats(Type, #proto_stats.recv_pkt, Pkt, #proto_stats.recv_msg, Msg, Stats); diff --git a/src/emqttd_ws_client.erl b/src/emqttd_ws_client.erl index 71cb4b344..c6ea5c1ad 100644 --- a/src/emqttd_ws_client.erl +++ b/src/emqttd_ws_client.erl @@ -14,6 +14,8 @@ %% limitations under the License. %%-------------------------------------------------------------------- +%% @doc MQTT WebSocket Connection. + -module(emqttd_ws_client). -behaviour(gen_server2). @@ -24,6 +26,8 @@ -include("emqttd_protocol.hrl"). +-import(proplists, [get_value/3]). + %% API Exports -export([start_link/4]). @@ -44,13 +48,14 @@ -export([prioritise_call/4, prioritise_info/3, handle_pre_hibernate/1]). %% WebSocket Client State --record(wsclient_state, {ws_pid, peer, connection, proto_state, keepalive, +-record(wsclient_state, {ws_pid, peername, connection, proto_state, keepalive, enable_stats}). -define(SOCK_STATS, [recv_oct, recv_cnt, send_oct, send_cnt, send_pend]). --define(WSLOG(Level, Peer, Format, Args), - lager:Level("WsClient(~s): " ++ Format, [Peer | Args])). +-define(WSLOG(Level, Format, Args, State), + lager:Level("WsClient(~s): " ++ Format, + [esockd_net:format(State#wsclient_state.peername) | Args])). %% @doc Start WebSocket Client. start_link(Env, WsPid, Req, ReplyChannel) -> @@ -84,22 +89,16 @@ init([Env, WsPid, Req, ReplyChannel]) -> {ok, Peername} = Req:get(peername), Headers = mochiweb_headers:to_list( mochiweb_request:get(headers, Req)), - %% SendFun = fun(Payload) -> ReplyChannel({binary, Payload}) end, - SendFun = fun(Packet) -> - Data = emqttd_serializer:serialize(Packet), - emqttd_metrics:inc('bytes/sent', iolist_size(Data)), - ReplyChannel({binary, Data}) - end, - EnableStats = proplists:get_value(client_enable_stats, Env, false), - ProtoState = emqttd_protocol:init(Peername, SendFun, + ProtoState = emqttd_protocol:init(Peername, send_fun(ReplyChannel), [{ws_initial_headers, Headers} | Env]), - IdleTimeout = proplists:get_value(client_idle_timeout, Env, 30000), + IdleTimeout = get_value(client_idle_timeout, Env, 30000), + EnableStats = get_value(client_enable_stats, Env, false), {ok, #wsclient_state{ws_pid = WsPid, - peer = Req:get(peer), + peername = Peername, connection = Req:get(connection), proto_state = ProtoState, enable_stats = EnableStats}, - IdleTimeout, {backoff, 1000, 1000, 5000}, ?MODULE}. + IdleTimeout, {backoff, 1000, 1000, 10000}, ?MODULE}. prioritise_call(Msg, _From, _Len, _State) -> case Msg of info -> 10; stats -> 10; state -> 10; _ -> 5 end. @@ -107,12 +106,12 @@ prioritise_call(Msg, _From, _Len, _State) -> prioritise_info(Msg, _Len, _State) -> case Msg of {redeliver, _} -> 5; _ -> 0 end. -handle_pre_hibernate(State = #wsclient_state{peer = Peer}) -> - io:format("WsClient(~s) will hibernate!~n", [Peer]), +handle_pre_hibernate(State) -> {hibernate, emit_stats(State)}. -handle_call(info, From, State = #wsclient_state{peer = Peer, proto_state = ProtoState}) -> - Info = [{websocket, true}, {peer, Peer} | emqttd_protocol:info(ProtoState)], +handle_call(info, From, State = #wsclient_state{peername = Peername, + proto_state = ProtoState}) -> + Info = [{websocket, true}, {peername, Peername} | emqttd_protocol:info(ProtoState)], {reply, Stats, _, _} = handle_call(stats, From, State), reply(lists:append(Info, Stats), State); @@ -127,17 +126,17 @@ handle_call(kick, _From, State) -> handle_call(session, _From, State = #wsclient_state{proto_state = ProtoState}) -> reply(emqttd_protocol:session(ProtoState), State); -handle_call(Req, _From, State = #wsclient_state{peer = Peer}) -> - ?WSLOG(error, Peer, "Unexpected request: ~p", [Req]), - reply({error, unsupported_request}, State). +handle_call(Req, _From, State) -> + ?WSLOG(error, "Unexpected request: ~p", [Req], State), + reply({error, unexpected_request}, State). -handle_cast({received, Packet}, State = #wsclient_state{peer = Peer, proto_state = ProtoState}) -> +handle_cast({received, Packet}, State = #wsclient_state{proto_state = ProtoState}) -> emqttd_metrics:received(Packet), case emqttd_protocol:received(Packet, ProtoState) of {ok, ProtoState1} -> {noreply, State#wsclient_state{proto_state = ProtoState1}, hibernate}; {error, Error} -> - ?WSLOG(error, Peer, "Protocol error - ~p", [Error]), + ?WSLOG(error, "Protocol error - ~p", [Error], State), shutdown(Error, State); {error, Error, ProtoState1} -> shutdown(Error, State#wsclient_state{proto_state = ProtoState1}); @@ -145,8 +144,8 @@ handle_cast({received, Packet}, State = #wsclient_state{peer = Peer, proto_state stop(Reason, State#wsclient_state{proto_state = ProtoState1}) end; -handle_cast(Msg, State = #wsclient_state{peer = Peer}) -> - ?WSLOG(error, Peer, "Unexpected msg: ~p", [Msg]), +handle_cast(Msg, State) -> + ?WSLOG(error, "Unexpected Msg: ~p", [Msg], State), {noreply, State, hibernate}. handle_info({subscribe, TopicTable}, State) -> @@ -186,43 +185,36 @@ handle_info(emit_stats, State) -> handle_info(timeout, State) -> shutdown(idle_timeout, State); -handle_info({shutdown, conflict, {ClientId, NewPid}}, State = #wsclient_state{peer = Peer}) -> - ?WSLOG(warning, Peer, "clientid '~s' conflict with ~p", [ClientId, NewPid]), +handle_info({shutdown, conflict, {ClientId, NewPid}}, State) -> + ?WSLOG(warning, "clientid '~s' conflict with ~p", [ClientId, NewPid], State), shutdown(conflict, State); -handle_info({keepalive, start, Interval}, State = #wsclient_state{peer = Peer, connection = Conn}) -> - ?WSLOG(debug, Peer, "Keepalive at the interval of ~p", [Interval]), - StatFun = fun() -> - case Conn:getstat([recv_oct]) of - {ok, [{recv_oct, RecvOct}]} -> {ok, RecvOct}; - {error, Error} -> {error, Error} - end - end, - KeepAlive = emqttd_keepalive:start(StatFun, Interval, {keepalive, check}), +handle_info({keepalive, start, Interval}, State = #wsclient_state{connection = Conn}) -> + ?WSLOG(debug, "Keepalive at the interval of ~p", [Interval], State), + KeepAlive = emqttd_keepalive:start(stat_fun(Conn), Interval, {keepalive, check}), {noreply, State#wsclient_state{keepalive = KeepAlive}, hibernate}; -handle_info({keepalive, check}, State = #wsclient_state{peer = Peer, - keepalive = KeepAlive}) -> +handle_info({keepalive, check}, State = #wsclient_state{keepalive = KeepAlive}) -> case emqttd_keepalive:check(KeepAlive) of {ok, KeepAlive1} -> {noreply, emit_stats(State#wsclient_state{keepalive = KeepAlive1}), hibernate}; {error, timeout} -> - ?WSLOG(debug, Peer, "Keepalive Timeout!", []), + ?WSLOG(debug, "Keepalive Timeout!", [], State), shutdown(keepalive_timeout, State); {error, Error} -> - ?WSLOG(warning, Peer, "Keepalive error - ~p", [Error]), + ?WSLOG(warning, "Keepalive error - ~p", [Error], State), shutdown(keepalive_error, State) end; handle_info({'EXIT', WsPid, normal}, State = #wsclient_state{ws_pid = WsPid}) -> stop(normal, State); -handle_info({'EXIT', WsPid, Reason}, State = #wsclient_state{peer = Peer, ws_pid = WsPid}) -> - ?WSLOG(error, Peer, "shutdown: ~p",[Reason]), +handle_info({'EXIT', WsPid, Reason}, State = #wsclient_state{ws_pid = WsPid}) -> + ?WSLOG(error, "shutdown: ~p",[Reason], State), shutdown(Reason, State); -handle_info(Info, State = #wsclient_state{peer = Peer}) -> - ?WSLOG(error, Peer, "Unexpected Info: ~p", [Info]), +handle_info(Info, State) -> + ?WSLOG(error, "Unexpected Info: ~p", [Info], State), {noreply, State, hibernate}. terminate(Reason, #wsclient_state{proto_state = ProtoState, keepalive = KeepAlive}) -> @@ -241,6 +233,21 @@ code_change(_OldVsn, State, _Extra) -> %% Internal functions %%-------------------------------------------------------------------- +send_fun(ReplyChannel) -> + fun(Packet) -> + Data = emqttd_serializer:serialize(Packet), + emqttd_metrics:inc('bytes/sent', iolist_size(Data)), + ReplyChannel({binary, Data}) + end. + +stat_fun(Conn) -> + fun() -> + case Conn:getstat([recv_oct]) of + {ok, [{recv_oct, RecvOct}]} -> {ok, RecvOct}; + {error, Error} -> {error, Error} + end + end. + emit_stats(State = #wsclient_state{proto_state = ProtoState}) -> emit_stats(emqttd_protocol:clientid(ProtoState), State). From 088adeda3bcb76e27121c60932c59c5e7bbedd9d Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Wed, 22 Feb 2017 14:11:10 +0800 Subject: [PATCH 064/125] Fix issue #916 - add 'mqtt_msg_from()' type --- include/emqttd.hrl | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/include/emqttd.hrl b/include/emqttd.hrl index 0d7d4c134..3f4f22229 100644 --- a/include/emqttd.hrl +++ b/include/emqttd.hrl @@ -108,13 +108,15 @@ -type(mqtt_pktid() :: 1..16#ffff | undefined). +-type(mqtt_msg_from() :: atom() | {binary(), undefined | binary()}). + -record(mqtt_message, { %% Global unique message ID id :: mqtt_msgid(), %% PacketId pktid :: mqtt_pktid(), %% ClientId and Username - from :: {binary(), undefined | binary()}, + from :: mqtt_msg_from(), %% Topic that the message is published to topic :: binary(), %% Message QoS @@ -127,12 +129,13 @@ dup = false :: boolean(), %% $SYS flag sys = false :: boolean(), + %% Headers headers = [] :: list(), %% Payload payload :: binary(), %% Timestamp timestamp :: erlang:timestamp() -}). + }). -type(mqtt_message() :: #mqtt_message{}). From b4936726fd368ecf911ef8ac519ee2b2e3e99eff Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Wed, 22 Feb 2017 15:36:06 +0800 Subject: [PATCH 065/125] Remove the random:seed/1 to fix the build warnings --- src/emqttd_time.erl | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/emqttd_time.erl b/src/emqttd_time.erl index f9ada795d..7e5940438 100644 --- a/src/emqttd_time.erl +++ b/src/emqttd_time.erl @@ -21,10 +21,7 @@ -export([seed/0, now_secs/0, now_secs/1, now_ms/0, now_ms/1, ts_from_ms/1]). seed() -> - case erlang:function_exported(erlang, timestamp, 0) of - true -> rand:seed(exsplus, erlang:timestamp()); %% R18 - false -> random:seed(os:timestamp()) %% Compress now() deprecated warning... - end. + rand:seed(exsplus, erlang:timestamp()). now_ms() -> now_ms(os:timestamp()). @@ -40,3 +37,4 @@ now_secs({MegaSecs, Secs, _MicroSecs}) -> ts_from_ms(Ms) -> {Ms div 1000000, Ms rem 1000000, 0}. + From 88c2b4eaa3cfe2d523fbe896aaaec66dd8484980 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Wed, 22 Feb 2017 15:43:24 +0800 Subject: [PATCH 066/125] Use the new emqttd_parser API to parse Websocket frame --- src/emqttd_ws.erl | 48 +++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/src/emqttd_ws.erl b/src/emqttd_ws.erl index 8c483344e..b292c39bc 100644 --- a/src/emqttd_ws.erl +++ b/src/emqttd_ws.erl @@ -18,13 +18,18 @@ -author("Feng Lee "). +-include("emqttd_protocol.hrl"). + +-import(proplists, [get_value/3]). + -export([handle_request/1, ws_loop/3]). %% WebSocket Loop State --record(wsocket_state, {peer, client_pid, packet_opts, parser_fun}). +-record(wsocket_state, {peername, client_pid, max_packet_size, parser}). --define(WSLOG(Level, Peer, Format, Args), - lager:Level("WsClient(~s): " ++ Format, [Peer | Args])). +-define(WSLOG(Level, Format, Args, State), + lager:Level("WsClient(~s): " ++ Format, + [esockd_net:format(State#wsocket_state.peername) | Args])). %%-------------------------------------------------------------------- %% Handle WebSocket Request @@ -32,18 +37,14 @@ %% @doc Handle WebSocket Request. handle_request(Req) -> - Peer = Req:get(peer), - {ok, PktOpts} = emqttd:env(protocol), - ParserFun = emqttd_parser:new(PktOpts), - {ReentryWs, ReplyChannel} = upgrade(Req), + {ok, Env} = emqttd:env(protocol), + PacketSize = get_value(max_packet_size, Env, ?MAX_PACKET_SIZE), + Parser = emqttd_parser:initial_state(PacketSize), + %% Upgrade WebSocket. + {ReentryWs, ReplyChannel} = mochiweb_websocket:upgrade_connection(Req, fun ?MODULE:ws_loop/3), {ok, ClientPid} = emqttd_ws_client_sup:start_client(self(), Req, ReplyChannel), - ReentryWs(#wsocket_state{peer = Peer, client_pid = ClientPid, - packet_opts = PktOpts, parser_fun = ParserFun}). - -%% @doc Upgrade WebSocket. -%% @private -upgrade(Req) -> - mochiweb_websocket:upgrade_connection(Req, fun ?MODULE:ws_loop/3). + ReentryWs(#wsocket_state{peername = Req:get(peername), parser = Parser, + max_packet_size = PacketSize, client_pid = ClientPid}). %%-------------------------------------------------------------------- %% Receive Loop @@ -54,25 +55,24 @@ ws_loop(<<>>, State, _ReplyChannel) -> State; ws_loop([<<>>], State, _ReplyChannel) -> State; -ws_loop(Data, State = #wsocket_state{peer = Peer, client_pid = ClientPid, - parser_fun = ParserFun}, ReplyChannel) -> - ?WSLOG(debug, Peer, "RECV ~p", [Data]), +ws_loop(Data, State = #wsocket_state{client_pid = ClientPid, parser = Parser}, ReplyChannel) -> + ?WSLOG(debug, "RECV ~p", [Data], State), emqttd_metrics:inc('bytes/received', iolist_size(Data)), - case catch ParserFun(iolist_to_binary(Data)) of + case catch emqttd_parser:parse(iolist_to_binary(Data), Parser) of {more, NewParser} -> - State#wsocket_state{parser_fun = NewParser}; + State#wsocket_state{parser = NewParser}; {ok, Packet, Rest} -> gen_server:cast(ClientPid, {received, Packet}), ws_loop(Rest, reset_parser(State), ReplyChannel); {error, Error} -> - ?WSLOG(error, Peer, "Frame error: ~p", [Error]), + ?WSLOG(error, "Frame error: ~p", [Error], State), exit({shutdown, Error}); {'EXIT', Reason} -> - ?WSLOG(error, Peer, "Frame error: ~p", [Reason]), - ?WSLOG(error, Peer, "Error data: ~p", [Data]), + ?WSLOG(error, "Frame error: ~p", [Reason], State), + ?WSLOG(error, "Error data: ~p", [Data], State), exit({shutdown, parser_error}) end. -reset_parser(State = #wsocket_state{packet_opts = PktOpts}) -> - State#wsocket_state{parser_fun = emqttd_parser:new(PktOpts)}. +reset_parser(State = #wsocket_state{max_packet_size = PacketSize}) -> + State#wsocket_state{parser = emqttd_parser:initial_state(PacketSize)}. From 7d16422d5c2417f5b16baa1587b7ecdc5daf2ef8 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Wed, 22 Feb 2017 15:56:12 +0800 Subject: [PATCH 067/125] Require R19+ to build since 2.1 release --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8dc7ea803..c2bbf5ebd 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ Documentation on [emqtt.io/docs/v2/](http://emqtt.io/docs/v2/install.html), [doc ## Build From Source -The *EMQ* broker requires Erlang/OTP R18+ to build. +The *EMQ* broker requires Erlang/OTP R19+ to build since 2.1 release. ``` git clone https://github.com/emqtt/emq-relx.git From 05e34fe78314c816537f1542157e89999e5f7ea1 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Wed, 22 Feb 2017 16:19:49 +0800 Subject: [PATCH 068/125] Move the 'cuttlefish' library from TEST_DEPS to BUILD_DEPS --- Makefile | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 1447dd30c..eee4adc4d 100644 --- a/Makefile +++ b/Makefile @@ -2,6 +2,8 @@ PROJECT = emqttd PROJECT_DESCRIPTION = Erlang MQTT Broker PROJECT_VERSION = 2.1 +NO_AUTOPATCH = cuttlefish + DEPS = gproc lager esockd mochiweb lager_syslog dep_gproc = git https://github.com/uwiger/gproc @@ -13,11 +15,11 @@ dep_lager_syslog = git https://github.com/basho/lager_syslog ERLC_OPTS += +'{parse_transform, lager_transform}' -TEST_DEPS = cuttlefish emqttc +BUILD_DEPS = cuttlefish dep_cuttlefish = git https://github.com/emqtt/cuttlefish -dep_emqttc = git https://github.com/emqtt/emqttc -NO_AUTOPATCH = cuttlefish +TEST_DEPS = emqttc +dep_emqttc = git https://github.com/emqtt/emqttc TEST_ERLC_OPTS += +debug_info TEST_ERLC_OPTS += +'{parse_transform, lager_transform}' @@ -38,5 +40,5 @@ include erlang.mk app:: rebar.config app.config:: - cuttlefish -l info -e etc/ -c etc/emq.conf -i priv/emq.schema -d data/ + ./deps/cuttlefish/cuttlefish -l info -e etc/ -c etc/emq.conf -i priv/emq.schema -d data/ From 03e6c8b64cd257e7fd5b90d28891b34405e7d139 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Wed, 22 Feb 2017 17:33:25 +0800 Subject: [PATCH 069/125] Version 2.1.0 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index eee4adc4d..913f11b89 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ PROJECT = emqttd PROJECT_DESCRIPTION = Erlang MQTT Broker -PROJECT_VERSION = 2.1 +PROJECT_VERSION = 2.1.0 NO_AUTOPATCH = cuttlefish From 8e7ea09cbd2a026bfab7c47ac9efbd983257e2e2 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Wed, 22 Feb 2017 17:34:00 +0800 Subject: [PATCH 070/125] Tune the mqtt.client|session.enable_stats off --- etc/emq.conf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/etc/emq.conf b/etc/emq.conf index 0e20dd60d..f042bd279 100644 --- a/etc/emq.conf +++ b/etc/emq.conf @@ -114,7 +114,7 @@ mqtt.max_packet_size = 64KB mqtt.client.idle_timeout = 30s ## Enable client Stats: on | off -mqtt.client.enable_stats = on +mqtt.client.enable_stats = off ##-------------------------------------------------------------------- ## MQTT Session @@ -137,7 +137,7 @@ mqtt.session.max_awaiting_rel = 100 mqtt.session.await_rel_timeout = 20s ## Enable Statistics: on | off -mqtt.session.enable_stats = on +mqtt.session.enable_stats = off ## Expired after 1 day: ## w - week From 39abdb8b41685e7fb714e64e5bb9fe0f6446cbcf Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Wed, 22 Feb 2017 17:52:55 +0800 Subject: [PATCH 071/125] Remove 'enable_stats' from 'INFO_KEYS', and remove io:format line --- src/emqttd_client.erl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/emqttd_client.erl b/src/emqttd_client.erl index 93efa938b..af36418d9 100644 --- a/src/emqttd_client.erl +++ b/src/emqttd_client.erl @@ -57,7 +57,7 @@ rate_limit, packet_size, parser, proto_state, keepalive, enable_stats}). --define(INFO_KEYS, [peername, conn_state, await_recv, enable_stats]). +-define(INFO_KEYS, [peername, conn_state, await_recv]). -define(SOCK_STATS, [recv_oct, recv_cnt, send_oct, send_cnt, send_pend]). @@ -146,8 +146,7 @@ prioritise_call(Msg, _From, _Len, _State) -> prioritise_info(Msg, _Len, _State) -> case Msg of {redeliver, _} -> 5; _ -> 0 end. -handle_pre_hibernate(State = #client_state{peername = Peername}) -> - io:format("Client(~s) will hibernate!~n", [esockd_net:format(Peername)]), +handle_pre_hibernate(State) -> {hibernate, emit_stats(State)}. handle_call(info, From, State = #client_state{proto_state = ProtoState}) -> From f955614f9dbf173f9c3a4d67abc5f0ab34916e5d Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Wed, 22 Feb 2017 18:40:17 +0800 Subject: [PATCH 072/125] Remove the io:format line --- src/emqttd_session.erl | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/emqttd_session.erl b/src/emqttd_session.erl index 852f46c5f..2a27f2906 100644 --- a/src/emqttd_session.erl +++ b/src/emqttd_session.erl @@ -333,8 +333,7 @@ prioritise_info(Msg, _Len, _State) -> _ -> 0 end. -handle_pre_hibernate(State = #state{client_id = ClientId}) -> - io:format("Session(~s) will hibernate!~n", [ClientId]), +handle_pre_hibernate(State) -> {hibernate, emit_stats(State)}. handle_call({publish, Msg = #mqtt_message{qos = ?QOS_2, pktid = PacketId}}, _From, @@ -539,8 +538,7 @@ handle_info({timeout, _Timer, expired}, State) -> shutdown(expired, State); handle_info({'EXIT', ClientPid, _Reason}, - State = #state{clean_sess = true, - client_pid = ClientPid}) -> + State = #state{clean_sess = true, client_pid = ClientPid}) -> {stop, normal, State}; handle_info({'EXIT', ClientPid, Reason}, From 5543b7224315a7c9c903b5e63c9a33441b3c8451 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Wed, 22 Feb 2017 18:41:02 +0800 Subject: [PATCH 073/125] Force to GC the Websocket Process --- src/emqttd_ws_client.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/emqttd_ws_client.erl b/src/emqttd_ws_client.erl index c6ea5c1ad..c7cff7cea 100644 --- a/src/emqttd_ws_client.erl +++ b/src/emqttd_ws_client.erl @@ -106,7 +106,8 @@ prioritise_call(Msg, _From, _Len, _State) -> prioritise_info(Msg, _Len, _State) -> case Msg of {redeliver, _} -> 5; _ -> 0 end. -handle_pre_hibernate(State) -> +handle_pre_hibernate(State = #wsclient_state{ws_pid = WsPid}) -> + erlang:garbage_collect(WsPid),%%TODO: [{async, RequestId}]?? {hibernate, emit_stats(State)}. handle_call(info, From, State = #wsclient_state{peername = Peername, From 5d5de51f89e3c964a89992431ff6c277183c2f0b Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Wed, 22 Feb 2017 18:47:14 +0800 Subject: [PATCH 074/125] Change the gen_server:start_link to gen_server2 --- src/emqttd_ws_client.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/emqttd_ws_client.erl b/src/emqttd_ws_client.erl index c7cff7cea..b4c9d4fe9 100644 --- a/src/emqttd_ws_client.erl +++ b/src/emqttd_ws_client.erl @@ -59,7 +59,7 @@ %% @doc Start WebSocket Client. start_link(Env, WsPid, Req, ReplyChannel) -> - gen_server:start_link(?MODULE, [Env, WsPid, Req, ReplyChannel], []). + gen_server2:start_link(?MODULE, [Env, WsPid, Req, ReplyChannel], []). info(CPid) -> gen_server2:call(CPid, info). From b8084f29891f746dcfd66dc0cdcbcaa2e1c13022 Mon Sep 17 00:00:00 2001 From: Feng Date: Wed, 22 Feb 2017 22:24:23 +0800 Subject: [PATCH 075/125] Remove inc_stats/1 function --- src/emqttd_misc.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/emqttd_misc.erl b/src/emqttd_misc.erl index 69cba9d8f..e60d27d4f 100644 --- a/src/emqttd_misc.erl +++ b/src/emqttd_misc.erl @@ -19,7 +19,7 @@ -author("Feng Lee "). -export([merge_opts/2, start_timer/2, start_timer/3, cancel_timer/1, - proc_stats/0, proc_stats/1, inc_stats/1]). + proc_stats/0, proc_stats/1]). %% @doc Merge Options merge_opts(Defaults, Options) -> @@ -53,13 +53,13 @@ cancel_timer(Timer) -> _ -> ok end. +-spec(proc_stats() -> list()). proc_stats() -> proc_stats(self()). +-spec(proc_stats(pid()) -> list()). proc_stats(Pid) -> Stats = process_info(Pid, [message_queue_len, heap_size, reductions]), {value, {_, V}, Stats1} = lists:keytake(message_queue_len, 1, Stats), [{mailbox_len, V} | Stats1]. -inc_stats(Key) -> put(Key, get(Key) + 1). - From 239cf1b5fcb6efbffac7a67297ee2b7e2bdbb640 Mon Sep 17 00:00:00 2001 From: Feng Date: Wed, 22 Feb 2017 23:07:27 +0800 Subject: [PATCH 076/125] Add '{backoff, 1000, 1000, 10000}' to return of init/1 --- src/emqttd_bridge.erl | 17 +++++++++-------- src/emqttd_pubsub.erl | 7 ++++--- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/emqttd_bridge.erl b/src/emqttd_bridge.erl index 7e36ad256..7b3015f6d 100644 --- a/src/emqttd_bridge.erl +++ b/src/emqttd_bridge.erl @@ -40,16 +40,16 @@ qos = ?QOS_2, topic_suffix = <<>>, topic_prefix = <<>>, - mqueue :: emqttd_mqueue:mqueue(), + mqueue :: emqttd_mqueue:mqueue(), max_queue_len = 10000, ping_down_interval = ?PING_DOWN_INTERVAL, status = up}). --type(option() :: {qos, mqtt_qos()} | - {topic_suffix, binary()} | - {topic_prefix, binary()} | - {max_queue_len, pos_integer()} | - {ping_down_interval, pos_integer()}). +-type(option() :: {qos, mqtt_qos()} | + {topic_suffix, binary()} | + {topic_prefix, binary()} | + {max_queue_len, pos_integer()} | + {ping_down_interval, pos_integer()}). -export_type([option/0]). @@ -79,9 +79,10 @@ init([Pool, Id, Node, Topic, Options]) -> MQueue = emqttd_mqueue:new(qname(Node, Topic), [{max_len, State#state.max_queue_len}], emqttd_alarm:alarm_fun()), - {ok, State#state{pool = Pool, id = Id, mqueue = MQueue}}; + {ok, State#state{pool = Pool, id = Id, mqueue = MQueue}, hibernate, + {backoff, 1000, 1000, 10000}}; false -> - {stop, {cannot_connect, Node}} + {stop, {cannot_connect_node, Node}} end. parse_opts([], State) -> diff --git a/src/emqttd_pubsub.erl b/src/emqttd_pubsub.erl index c99aca513..393e4b788 100644 --- a/src/emqttd_pubsub.erl +++ b/src/emqttd_pubsub.erl @@ -164,11 +164,12 @@ pick(Subscriber) -> init([Pool, Id, Env]) -> ?GPROC_POOL(join, Pool, Id), - {ok, #state{pool = Pool, id = Id, env = Env}}. + {ok, #state{pool = Pool, id = Id, env = Env}, hibernate, + {backoff, 2000, 2000, 20000}}. handle_call({subscribe, Topic, Subscriber, Options}, _From, State) -> add_subscriber(Topic, Subscriber, Options), - {reply, ok, setstats(State)}; + {reply, ok, setstats(State), hibernate}; handle_call({unsubscribe, Topic, Subscriber, Options}, _From, State) -> del_subscriber(Topic, Subscriber, Options), @@ -179,7 +180,7 @@ handle_call(Req, _From, State) -> handle_cast({subscribe, Topic, Subscriber, Options}, State) -> add_subscriber(Topic, Subscriber, Options), - {noreply, setstats(State)}; + {noreply, setstats(State), hibernate}; handle_cast({unsubscribe, Topic, Subscriber, Options}, State) -> del_subscriber(Topic, Subscriber, Options), From 365bfb9e80f79e03cf65239e509aa656c085432b Mon Sep 17 00:00:00 2001 From: Feng Date: Wed, 22 Feb 2017 23:08:22 +0800 Subject: [PATCH 077/125] Replace emqttd_misc:inc_stats/1 with inc_stats/1 --- src/emqttd_session.erl | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/emqttd_session.erl b/src/emqttd_session.erl index 2a27f2906..06f988bb7 100644 --- a/src/emqttd_session.erl +++ b/src/emqttd_session.erl @@ -301,7 +301,7 @@ init([CleanSess, {ClientId, Username}, ClientPid]) -> created_at = os:timestamp()}, emqttd_sm:register_session(ClientId, CleanSess, info(State)), emqttd_hooks:run('session.created', [ClientId, Username]), - {ok, emit_stats(State), hibernate, {backoff, 1000, 1000, 10000}, ?MODULE}. + {ok, emit_stats(State), hibernate, {backoff, 1000, 1000, 10000}}. init_stats(Keys) -> lists:foreach(fun(K) -> put(K, 0) end, Keys). @@ -690,7 +690,7 @@ dispatch(Msg = #mqtt_message{qos = QoS}, end. enqueue_msg(Msg, State = #state{mqueue = Q}) -> - emqttd_misc:inc_stats(enqueue_msg), + inc_stats(enqueue_msg), State#state{mqueue = emqttd_mqueue:in(Msg, Q)}. %%-------------------------------------------------------------------- @@ -701,7 +701,7 @@ redeliver(Msg = #mqtt_message{qos = QoS}, State) -> deliver(Msg#mqtt_message{dup = if QoS =:= ?QOS2 -> false; true -> true end}, State). deliver(Msg, #state{client_pid = Pid}) -> - emqttd_misc:inc_stats(deliver_msg), + inc_stats(deliver_msg), Pid ! {deliver, Msg}. %%-------------------------------------------------------------------- @@ -793,6 +793,8 @@ emit_stats(State = #state{client_id = ClientId}) -> emqttd_stats:set_session_stats(ClientId, stats(State)), State. +inc_stats(Key) -> put(Key, get(Key) + 1). + %%-------------------------------------------------------------------- %% Helper functions %%-------------------------------------------------------------------- From 2d9dbe472948c0fe0b77f763901ad0e47c869fe7 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Thu, 23 Feb 2017 11:55:52 +0800 Subject: [PATCH 078/125] Support to hook 'tag' function --- src/emqttd.erl | 21 ++++---- src/emqttd_hooks.erl | 114 +++++++++++++++++++++++++----------------- test/emqttd_SUITE.erl | 44 ++++++++-------- 3 files changed, 103 insertions(+), 76 deletions(-) diff --git a/src/emqttd.erl b/src/emqttd.erl index 63b53d4e1..037c0de1a 100644 --- a/src/emqttd.erl +++ b/src/emqttd.erl @@ -138,17 +138,20 @@ subscriber_down(Subscriber) -> %% Hooks API %%-------------------------------------------------------------------- --spec(hook(atom(), function(), list(any())) -> ok | {error, any()}). -hook(Hook, Function, InitArgs) -> - emqttd_hooks:add(Hook, Function, InitArgs). +-spec(hook(atom(), function() | {emqttd_hooks:hooktag(), function()}, list(any())) + -> ok | {error, any()}). +hook(Hook, TagFunction, InitArgs) -> + emqttd_hooks:add(Hook, TagFunction, InitArgs). --spec(hook(atom(), function(), list(any()), integer()) -> ok | {error, any()}). -hook(Hook, Function, InitArgs, Priority) -> - emqttd_hooks:add(Hook, Function, InitArgs, Priority). +-spec(hook(atom(), function() | {emqttd_hooks:hooktag(), function()}, list(any()), integer()) + -> ok | {error, any()}). +hook(Hook, TagFunction, InitArgs, Priority) -> + emqttd_hooks:add(Hook, TagFunction, InitArgs, Priority). --spec(unhook(atom(), function()) -> ok | {error, any()}). -unhook(Hook, Function) -> - emqttd_hooks:delete(Hook, Function). +-spec(unhook(atom(), function() | {emqttd_hooks:hooktag(), function()}) + -> ok | {error, any()}). +unhook(Hook, TagFunction) -> + emqttd_hooks:delete(Hook, TagFunction). -spec(run_hooks(atom(), list(any())) -> ok | stop). run_hooks(Hook, Args) -> diff --git a/src/emqttd_hooks.erl b/src/emqttd_hooks.erl index ce2691894..693a67ff7 100644 --- a/src/emqttd_hooks.erl +++ b/src/emqttd_hooks.erl @@ -32,7 +32,12 @@ -record(state, {}). --record(callback, {function :: function(), +-type(hooktag() :: atom() | string() | binary()). + +-export_type([hooktag/0]). + +-record(callback, {tag :: hooktag(), + function :: function(), init_args = [] :: list(any()), priority = 0 :: integer()}). @@ -47,17 +52,24 @@ start_link() -> %% Hooks API %%-------------------------------------------------------------------- --spec(add(atom(), function(), list(any())) -> ok). -add(HookPoint, Function, InitArgs) -> - add(HookPoint, Function, InitArgs, 0). +-spec(add(atom(), function() | {hooktag(), function()}, list(any())) -> ok). +add(HookPoint, Function, InitArgs) when is_function(Function) -> + add(HookPoint, {undefined, Function}, InitArgs, 0); --spec(add(atom(), function(), list(any()), integer()) -> ok). -add(HookPoint, Function, InitArgs, Priority) -> - gen_server:call(?MODULE, {add, HookPoint, Function, InitArgs, Priority}). +add(HookPoint, {Tag, Function}, InitArgs) when is_function(Function) -> + add(HookPoint, {Tag, Function}, InitArgs, 0). --spec(delete(atom(), function()) -> ok). -delete(HookPoint, Function) -> - gen_server:call(?MODULE, {delete, HookPoint, Function}). +-spec(add(atom(), function() | {hooktag(), function()}, list(any()), integer()) -> ok). +add(HookPoint, Function, InitArgs, Priority) when is_function(Function) -> + add(HookPoint, {undefined, Function}, InitArgs, Priority); +add(HookPoint, {Tag, Function}, InitArgs, Priority) when is_function(Function) -> + gen_server:call(?MODULE, {add, HookPoint, {Tag, Function}, InitArgs, Priority}). + +-spec(delete(atom(), function() | {hooktag(), function()}) -> ok). +delete(HookPoint, Function) when is_function(Function) -> + delete(HookPoint, {undefined, Function}); +delete(HookPoint, {Tag, Function}) when is_function(Function) -> + gen_server:call(?MODULE, {delete, HookPoint, {Tag, Function}}). %% @doc Run hooks without Acc. -spec(run(atom(), list(Arg :: any())) -> ok | stop). @@ -85,7 +97,8 @@ run_([#callback{function = Fun, init_args = InitArgs} | Callbacks], Args, Acc) - ok -> run_(Callbacks, Args, Acc); {ok, NewAcc} -> run_(Callbacks, Args, NewAcc); stop -> {stop, Acc}; - {stop, NewAcc} -> {stop, NewAcc} + {stop, NewAcc} -> {stop, NewAcc}; + _Any -> run_(Callbacks, Args, Acc) end; run_([], _Args, Acc) -> @@ -94,8 +107,8 @@ run_([], _Args, Acc) -> -spec(lookup(atom()) -> [#callback{}]). lookup(HookPoint) -> case ets:lookup(?HOOK_TAB, HookPoint) of - [] -> []; - [#hook{callbacks = Callbacks}] -> Callbacks + [#hook{callbacks = Callbacks}] -> Callbacks; + [] -> [] end. %%-------------------------------------------------------------------- @@ -106,39 +119,38 @@ init([]) -> ets:new(?HOOK_TAB, [set, protected, named_table, {keypos, #hook.name}]), {ok, #state{}}. -handle_call({add, HookPoint, Function, InitArgs, Priority}, _From, State) -> - Reply = - case ets:lookup(?HOOK_TAB, HookPoint) of - [#hook{callbacks = Callbacks}] -> - case lists:keyfind(Function, #callback.function, Callbacks) of - false -> - Callback = #callback{function = Function, - init_args = InitArgs, - priority = Priority}, - insert_hook_(HookPoint, add_callback_(Callback, Callbacks)); - _Callback -> - {error, already_hooked} - end; - [] -> - Callback = #callback{function = Function, - init_args = InitArgs, - priority = Priority}, - insert_hook_(HookPoint, [Callback]) - end, - {reply, Reply, State}; +handle_call({add, HookPoint, {Tag, Function}, InitArgs, Priority}, _From, State) -> + Callback = #callback{tag = Tag, function = Function, + init_args = InitArgs, priority = Priority}, + {reply, + case ets:lookup(?HOOK_TAB, HookPoint) of + [#hook{callbacks = Callbacks}] -> + case contain_(Tag, Function, Callbacks) of + false -> + insert_hook_(HookPoint, add_callback_(Callback, Callbacks)); + true -> + {error, already_hooked} + end; + [] -> + insert_hook_(HookPoint, [Callback]) + end, State}; -handle_call({delete, HookPoint, Function}, _From, State) -> - Reply = - case ets:lookup(?HOOK_TAB, HookPoint) of - [#hook{callbacks = Callbacks}] -> - insert_hook_(HookPoint, del_callback_(Function, Callbacks)); - [] -> - {error, not_found} - end, - {reply, Reply, State}; +handle_call({delete, HookPoint, {Tag, Function}}, _From, State) -> + {reply, + case ets:lookup(?HOOK_TAB, HookPoint) of + [#hook{callbacks = Callbacks}] -> + case contain_(Tag, Function, Callbacks) of + true -> + insert_hook_(HookPoint, del_callback_(Tag, Function, Callbacks)); + false -> + {error, not_found} + end; + [] -> + {error, not_found} + end, State}; -handle_call(_Req, _From, State) -> - {reply, ignore, State}. +handle_call(Req, _From, State) -> + {reply, {error, {unexpected_request, Req}}, State}. handle_cast(_Msg, State) -> {noreply, State}. @@ -162,6 +174,16 @@ insert_hook_(HookPoint, Callbacks) -> add_callback_(Callback, Callbacks) -> lists:keymerge(#callback.priority, Callbacks, [Callback]). -del_callback_(Function, Callbacks) -> - lists:keydelete(Function, #callback.function, Callbacks). +del_callback_(Tag, Function, Callbacks) -> + lists:filter( + fun(#callback{tag = Tag1, function = Func1}) -> + not ((Tag =:= Tag1) andalso (Function =:= Func1)) + end, Callbacks). + +contain_(_Tag, _Function, []) -> + false; +contain_(Tag, Function, [#callback{tag = Tag, function = Function}|_Callbacks]) -> + true; +contain_(Tag, Function, [_Callback | Callbacks]) -> + contain_(Tag, Function, Callbacks). diff --git a/test/emqttd_SUITE.erl b/test/emqttd_SUITE.erl index d7af619df..afa1a1f06 100644 --- a/test/emqttd_SUITE.erl +++ b/test/emqttd_SUITE.erl @@ -366,37 +366,39 @@ set_get_stat(_) -> %%-------------------------------------------------------------------- add_delete_hook(_) -> - emqttd:hook(test_hook, fun ?MODULE:hook_fun1/1, []), - emqttd:hook(test_hook, fun ?MODULE:hook_fun2/1, []), - {error, already_hooked} = emqttd:hook(test_hook, fun ?MODULE:hook_fun2/1, []), - Callbacks = [{callback, fun ?MODULE:hook_fun1/1, [], 0}, - {callback, fun ?MODULE:hook_fun2/1, [], 0}], + ok = emqttd:hook(test_hook, fun ?MODULE:hook_fun1/1, []), + ok = emqttd:hook(test_hook, {tag, fun ?MODULE:hook_fun2/1}, []), + {error, already_hooked} = emqttd:hook(test_hook, {tag, fun ?MODULE:hook_fun2/1}, []), + Callbacks = [{callback, undefined, fun ?MODULE:hook_fun1/1, [], 0}, + {callback, tag, fun ?MODULE:hook_fun2/1, [], 0}], Callbacks = emqttd_hooks:lookup(test_hook), - emqttd:unhook(test_hook, fun ?MODULE:hook_fun1/1), - emqttd:unhook(test_hook, fun ?MODULE:hook_fun2/1), - ok = emqttd:unhook(test_hook, fun ?MODULE:hook_fun2/1), - {error, not_found} = emqttd:unhook(test_hook1, fun ?MODULE:hook_fun2/1), + ok = emqttd:unhook(test_hook, fun ?MODULE:hook_fun1/1), + ct:print("Callbacks: ~p~n", [emqttd_hooks:lookup(test_hook)]), + ok = emqttd:unhook(test_hook, {tag, fun ?MODULE:hook_fun2/1}), + {error, not_found} = emqttd:unhook(test_hook1, {tag, fun ?MODULE:hook_fun2/1}), [] = emqttd_hooks:lookup(test_hook), - emqttd:hook(emqttd_hook, fun ?MODULE:hook_fun1/1, [], 9), - emqttd:hook(emqttd_hook, fun ?MODULE:hook_fun2/1, [], 8), - Callbacks2 = [{callback, fun ?MODULE:hook_fun2/1, [], 8}, - {callback, fun ?MODULE:hook_fun1/1, [], 9}], + ok = emqttd:hook(emqttd_hook, fun ?MODULE:hook_fun1/1, [], 9), + ok = emqttd:hook(emqttd_hook, {"tag", fun ?MODULE:hook_fun2/1}, [], 8), + Callbacks2 = [{callback, "tag", fun ?MODULE:hook_fun2/1, [], 8}, + {callback, undefined, fun ?MODULE:hook_fun1/1, [], 9}], Callbacks2 = emqttd_hooks:lookup(emqttd_hook), - emqttd:unhook(emqttd_hook, fun ?MODULE:hook_fun1/1), - emqttd:unhook(emqttd_hook, fun ?MODULE:hook_fun2/1), + ok = emqttd:unhook(emqttd_hook, fun ?MODULE:hook_fun1/1), + ok = emqttd:unhook(emqttd_hook, {"tag", fun ?MODULE:hook_fun2/1}), [] = emqttd_hooks:lookup(emqttd_hook). run_hooks(_) -> - emqttd:hook(foldl_hook, fun ?MODULE:hook_fun3/4, [init]), - emqttd:hook(foldl_hook, fun ?MODULE:hook_fun4/4, [init]), - emqttd:hook(foldl_hook, fun ?MODULE:hook_fun5/4, [init]), + ok = emqttd:hook(foldl_hook, fun ?MODULE:hook_fun3/4, [init]), + ok = emqttd:hook(foldl_hook, {tag, fun ?MODULE:hook_fun3/4}, [init]), + ok = emqttd:hook(foldl_hook, fun ?MODULE:hook_fun4/4, [init]), + ok = emqttd:hook(foldl_hook, fun ?MODULE:hook_fun5/4, [init]), {stop, [r3, r2]} = emqttd:run_hooks(foldl_hook, [arg1, arg2], []), {ok, []} = emqttd:run_hooks(unknown_hook, [], []), - emqttd:hook(foreach_hook, fun ?MODULE:hook_fun6/2, [initArg]), - emqttd:hook(foreach_hook, fun ?MODULE:hook_fun7/2, [initArg]), - emqttd:hook(foreach_hook, fun ?MODULE:hook_fun8/2, [initArg]), + ok = emqttd:hook(foreach_hook, fun ?MODULE:hook_fun6/2, [initArg]), + ok = emqttd:hook(foreach_hook, {tag, fun ?MODULE:hook_fun6/2}, [initArg]), + ok = emqttd:hook(foreach_hook, fun ?MODULE:hook_fun7/2, [initArg]), + ok = emqttd:hook(foreach_hook, fun ?MODULE:hook_fun8/2, [initArg]), stop = emqttd:run_hooks(foreach_hook, [arg]). hook_fun1([]) -> ok. From ab76e7978b3d20adc844f49cb512b7e2114c920b Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Thu, 23 Feb 2017 16:53:09 +0800 Subject: [PATCH 079/125] Add emqttd_gc module --- src/emqttd_gc.erl | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 src/emqttd_gc.erl diff --git a/src/emqttd_gc.erl b/src/emqttd_gc.erl new file mode 100644 index 000000000..339e895f3 --- /dev/null +++ b/src/emqttd_gc.erl @@ -0,0 +1,46 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) +%% +%% 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. +%%-------------------------------------------------------------------- + +%% GC Utility functions. + +-module(emqttd_gc). + +-author("Feng Lee "). + +-export([conn_max_gc_count/0, reset_conn_gc_count/2, maybe_force_gc/2]). + +-spec(conn_max_gc_count() -> integer()). +conn_max_gc_count() -> + case emqttd:env(conn_force_gc_count) of + undefined -> undefined; + I when I > 0 -> I + rand:uniform(I) + end. + +-spec(reset_conn_gc_count(pos_integer(), tuple()) -> tuple()). +reset_conn_gc_count(Pos, State) -> + case element(Pos, State) of + undefined -> State; + _I -> setelement(Pos, State, conn_max_gc_count()) + end. + +maybe_force_gc(Pos, State) -> + case element(Pos, State) of + undefined -> State; + I when I =< 0 -> garbage_collect(), + reset_conn_gc_count(Pos, State); + I -> setelement(Pos, State, I - 1) + end. + From 124aa454fbd5bfb6610bcfd51e4ea59aba6889d0 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Thu, 23 Feb 2017 16:53:47 +0800 Subject: [PATCH 080/125] Add 'mqtt.conn.force_gc_count' config to tune GC of MQTT connection --- etc/emq.conf | 7 +++++++ priv/emq.schema | 9 +++++++++ 2 files changed, 16 insertions(+) diff --git a/etc/emq.conf b/etc/emq.conf index f042bd279..4c3ab73c9 100644 --- a/etc/emq.conf +++ b/etc/emq.conf @@ -106,6 +106,13 @@ mqtt.max_clientid_len = 1024 ## Max Packet Size Allowed, 64K by default. mqtt.max_packet_size = 64KB +##-------------------------------------------------------------------- +## MQTT Connection +##-------------------------------------------------------------------- + +## Force GC: pos_integer +mqtt.conn.force_gc_count = 100 + ##-------------------------------------------------------------------- ## MQTT Client ##-------------------------------------------------------------------- diff --git a/priv/emq.schema b/priv/emq.schema index 1e3ba1778..92ca118d2 100644 --- a/priv/emq.schema +++ b/priv/emq.schema @@ -316,6 +316,15 @@ end}. {max_packet_size, cuttlefish:conf_get("mqtt.max_packet_size", Conf)}] end}. +%%-------------------------------------------------------------------- +%% MQTT Connection +%%-------------------------------------------------------------------- + +%% @doc Force the client to GC: integer +{mapping, "mqtt.conn.force_gc_count", "emqttd.conn_force_gc_count", [ + {datatype, integer} +]}. + %%-------------------------------------------------------------------- %% MQTT Client %%-------------------------------------------------------------------- From 7d65ad42add1ba667b10b5aa6dc7bfe22fdb5081 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Thu, 23 Feb 2017 16:56:16 +0800 Subject: [PATCH 081/125] Add '[{fullsweep_after, 10}]' opts and 'force_gc_count' to tune the memory usage --- src/emqttd_client.erl | 32 ++++++++++++++++++-------------- src/emqttd_session.erl | 20 +++++++++++++++----- src/emqttd_ws_client.erl | 28 +++++++++++++++++----------- 3 files changed, 50 insertions(+), 30 deletions(-) diff --git a/src/emqttd_client.erl b/src/emqttd_client.erl index af36418d9..3cc7b341d 100644 --- a/src/emqttd_client.erl +++ b/src/emqttd_client.erl @@ -55,7 +55,7 @@ %% Unused fields: connname, peerhost, peerport -record(client_state, {connection, peername, conn_state, await_recv, rate_limit, packet_size, parser, proto_state, - keepalive, enable_stats}). + keepalive, enable_stats, force_gc_count}). -define(INFO_KEYS, [peername, conn_state, await_recv]). @@ -66,7 +66,7 @@ [esockd_net:format(State#client_state.peername) | Args])). start_link(Conn, Env) -> - {ok, proc_lib:spawn_link(?MODULE, init, [[Conn, Env]])}. + {ok, proc_lib:spawn_link(?MODULE, init, [[Conn, Env]], [{fullsweep_after, 10}])}. info(CPid) -> gen_server2:call(CPid, info). @@ -114,15 +114,17 @@ do_init(Conn, Env, Peername) -> Parser = emqttd_parser:initial_state(PacketSize), ProtoState = emqttd_protocol:init(Peername, SendFun, Env), EnableStats = get_value(client_enable_stats, Env, false), - State = run_socket(#client_state{connection = Conn, - peername = Peername, - await_recv = false, - conn_state = running, - rate_limit = RateLimit, - packet_size = PacketSize, - parser = Parser, - proto_state = ProtoState, - enable_stats = EnableStats}), + ForceGcCount = emqttd_gc:conn_max_gc_count(), + State = run_socket(#client_state{connection = Conn, + peername = Peername, + await_recv = false, + conn_state = running, + rate_limit = RateLimit, + packet_size = PacketSize, + parser = Parser, + proto_state = ProtoState, + enable_stats = EnableStats, + force_gc_count = ForceGcCount}), IdleTimout = get_value(client_idle_timeout, Env, 30000), gen_server2:enter_loop(?MODULE, [], State, self(), IdleTimout, {backoff, 1000, 1000, 10000}). @@ -147,7 +149,7 @@ prioritise_info(Msg, _Len, _State) -> case Msg of {redeliver, _} -> 5; _ -> 0 end. handle_pre_hibernate(State) -> - {hibernate, emit_stats(State)}. + {hibernate, emit_stats(emqttd_gc:reset_conn_gc_count(State))}. handle_call(info, From, State = #client_state{proto_state = ProtoState}) -> ProtoInfo = emqttd_protocol:info(ProtoState), @@ -237,7 +239,7 @@ handle_info({inet_async, _Sock, _Ref, {error, Reason}}, State) -> shutdown(Reason, State); handle_info({inet_reply, _Sock, ok}, State) -> - {noreply, State, hibernate}; + {noreply, gc(State), hibernate}; %% Tune GC handle_info({inet_reply, _Sock, {error, Reason}}, State) -> shutdown(Reason, State); @@ -291,7 +293,7 @@ code_change(_OldVsn, State, _Extra) -> %% Receive and parse tcp data received(<<>>, State) -> - {noreply, State, hibernate}; + {noreply, gc(State), hibernate}; received(Bytes, State = #client_state{parser = Parser, packet_size = PacketSize, @@ -370,3 +372,5 @@ shutdown(Reason, State) -> stop(Reason, State) -> {stop, Reason, State}. +gc(State) -> + emqttd_gc:maybe_force_gc(#client_state.force_gc_count, State). diff --git a/src/emqttd_session.erl b/src/emqttd_session.erl index 06f988bb7..1c575b3ed 100644 --- a/src/emqttd_session.erl +++ b/src/emqttd_session.erl @@ -147,6 +147,9 @@ %% Enable Stats enable_stats :: boolean(), + %% Force GC Count + force_gc_count :: undefined | integer(), + created_at :: erlang:timestamp() }). @@ -157,7 +160,8 @@ -define(STATE_KEYS, [clean_sess, client_id, username, binding, client_pid, old_client_pid, next_msg_id, max_subscriptions, subscriptions, upgrade_qos, inflight, max_inflight, retry_interval, mqueue, awaiting_rel, max_awaiting_rel, - await_rel_timeout, expiry_interval, enable_stats, created_at]). + await_rel_timeout, expiry_interval, enable_stats, force_gc_count, + created_at]). -define(LOG(Level, Format, Args, State), lager:Level([{client, State#state.client_id}], @@ -166,7 +170,8 @@ %% @doc Start a Session -spec(start_link(boolean(), {mqtt_client_id(), mqtt_username()}, pid()) -> {ok, pid()} | {error, any()}). start_link(CleanSess, {ClientId, Username}, ClientPid) -> - gen_server2:start_link(?MODULE, [CleanSess, {ClientId, Username}, ClientPid], []). + gen_server2:start_link(?MODULE, [CleanSess, {ClientId, Username}, ClientPid], + [{fullsweep_after, 10}]). %% Tune GC. %%-------------------------------------------------------------------- %% PubSub API @@ -280,6 +285,7 @@ init([CleanSess, {ClientId, Username}, ClientPid]) -> {ok, QEnv} = emqttd:env(queue), MaxInflight = get_value(max_inflight, Env, 0), EnableStats = get_value(enable_stats, Env, false), + ForceGcCount = emqttd_gc:conn_max_gc_count(), MQueue = emqttd_mqueue:new(ClientId, QEnv, emqttd_alarm:alarm_fun()), State = #state{clean_sess = CleanSess, binding = binding(ClientPid), @@ -298,6 +304,7 @@ init([CleanSess, {ClientId, Username}, ClientPid]) -> max_awaiting_rel = get_value(max_awaiting_rel, Env), expiry_interval = get_value(expiry_interval, Env), enable_stats = EnableStats, + force_gc_count = ForceGcCount, created_at = os:timestamp()}, emqttd_sm:register_session(ClientId, CleanSess, info(State)), emqttd_hooks:run('session.created', [ClientId, Username]), @@ -334,7 +341,7 @@ prioritise_info(Msg, _Len, _State) -> end. handle_pre_hibernate(State) -> - {hibernate, emit_stats(State)}. + {hibernate, emit_stats(emqttd_gc:reset_conn_gc_count(State))}. handle_call({publish, Msg = #mqtt_message{qos = ?QOS_2, pktid = PacketId}}, _From, State = #state{awaiting_rel = AwaitingRel, @@ -443,7 +450,7 @@ handle_cast({pubrel, PacketId}, State = #state{awaiting_rel = AwaitingRel}) -> case maps:take(PacketId, AwaitingRel) of {Msg, AwaitingRel1} -> spawn(emqttd_server, publish, [Msg]), %%:) - State#state{awaiting_rel = AwaitingRel1}; + gc(State#state{awaiting_rel = AwaitingRel1}); error -> ?LOG(warning, "Cannot find PUBREL: ~p", [PacketId], State), emqttd_metrics:inc('packets/pubrel/missed'), @@ -521,7 +528,7 @@ handle_cast(Msg, State) -> %% Dispatch Message handle_info({dispatch, Topic, Msg}, State) when is_record(Msg, mqtt_message) -> - {noreply, dispatch(tune_qos(Topic, Msg, State), State), hibernate}; + {noreply, gc(dispatch(tune_qos(Topic, Msg, State), State)), hibernate}; %% Do nothing if the client has been disconnected. handle_info({timeout, _Timer, retry_delivery}, State = #state{client_pid = undefined}) -> @@ -808,3 +815,6 @@ hibernate(State) -> shutdown(Reason, State) -> {stop, {shutdown, Reason}, State}. +gc(State) -> + emqttd_gc:maybe_force_gc(#state.force_gc_count, State). + diff --git a/src/emqttd_ws_client.erl b/src/emqttd_ws_client.erl index b4c9d4fe9..4faa44624 100644 --- a/src/emqttd_ws_client.erl +++ b/src/emqttd_ws_client.erl @@ -49,7 +49,7 @@ %% WebSocket Client State -record(wsclient_state, {ws_pid, peername, connection, proto_state, keepalive, - enable_stats}). + enable_stats, force_gc_count}). -define(SOCK_STATS, [recv_oct, recv_cnt, send_oct, send_cnt, send_pend]). @@ -59,7 +59,8 @@ %% @doc Start WebSocket Client. start_link(Env, WsPid, Req, ReplyChannel) -> - gen_server2:start_link(?MODULE, [Env, WsPid, Req, ReplyChannel], []). + gen_server2:start_link(?MODULE, [Env, WsPid, Req, ReplyChannel], + [{fullsweep_after, 10}]). %% Tune GC. info(CPid) -> gen_server2:call(CPid, info). @@ -93,11 +94,13 @@ init([Env, WsPid, Req, ReplyChannel]) -> [{ws_initial_headers, Headers} | Env]), IdleTimeout = get_value(client_idle_timeout, Env, 30000), EnableStats = get_value(client_enable_stats, Env, false), - {ok, #wsclient_state{ws_pid = WsPid, - peername = Peername, - connection = Req:get(connection), - proto_state = ProtoState, - enable_stats = EnableStats}, + ForceGcCount = emqttd_gc:conn_max_gc_count(), + {ok, #wsclient_state{ws_pid = WsPid, + peername = Peername, + connection = Req:get(connection), + proto_state = ProtoState, + enable_stats = EnableStats, + force_gc_count = ForceGcCount}, IdleTimeout, {backoff, 1000, 1000, 10000}, ?MODULE}. prioritise_call(Msg, _From, _Len, _State) -> @@ -108,7 +111,7 @@ prioritise_info(Msg, _Len, _State) -> handle_pre_hibernate(State = #wsclient_state{ws_pid = WsPid}) -> erlang:garbage_collect(WsPid),%%TODO: [{async, RequestId}]?? - {hibernate, emit_stats(State)}. + {hibernate, emqttd_gc:reset_conn_gc_count(emit_stats(State))}. handle_call(info, From, State = #wsclient_state{peername = Peername, proto_state = ProtoState}) -> @@ -135,7 +138,7 @@ handle_cast({received, Packet}, State = #wsclient_state{proto_state = ProtoState emqttd_metrics:received(Packet), case emqttd_protocol:received(Packet, ProtoState) of {ok, ProtoState1} -> - {noreply, State#wsclient_state{proto_state = ProtoState1}, hibernate}; + {noreply, gc(State#wsclient_state{proto_state = ProtoState1}), hibernate}; {error, Error} -> ?WSLOG(error, "Protocol error - ~p", [Error], State), shutdown(Error, State); @@ -172,7 +175,7 @@ handle_info({deliver, Message}, State) -> with_proto( fun(ProtoState) -> emqttd_protocol:send(Message, ProtoState) - end, State); + end, gc(State)); handle_info({redeliver, {?PUBREL, PacketId}}, State) -> with_proto( @@ -277,6 +280,9 @@ reply(Reply, State) -> shutdown(Reason, State) -> stop({shutdown, Reason}, State). -stop(Reason, State ) -> +stop(Reason, State) -> {stop, Reason, State}. +gc(State) -> + emqttd_gc:maybe_force_gc(#wsclient_state.force_gc_count, State). + From 73847b96fcd0eed8f6317fe7ca481cd7b50f634d Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Thu, 23 Feb 2017 17:16:55 +0800 Subject: [PATCH 082/125] Add 'FULLSWEEP_OPTS' macro --- include/emqttd_internal.hrl | 2 ++ src/emqttd_client.erl | 3 ++- src/emqttd_session.erl | 2 +- src/emqttd_ws_client.erl | 4 +++- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/include/emqttd_internal.hrl b/include/emqttd_internal.hrl index ec5fb3e73..fc482313f 100644 --- a/include/emqttd_internal.hrl +++ b/include/emqttd_internal.hrl @@ -58,3 +58,5 @@ false-> (FalseFun) end)). +-define(FULLSWEEP_OPTS, [{fullsweep_after, 10}]). + diff --git a/src/emqttd_client.erl b/src/emqttd_client.erl index 3cc7b341d..cd7ab45d0 100644 --- a/src/emqttd_client.erl +++ b/src/emqttd_client.erl @@ -66,7 +66,8 @@ [esockd_net:format(State#client_state.peername) | Args])). start_link(Conn, Env) -> - {ok, proc_lib:spawn_link(?MODULE, init, [[Conn, Env]], [{fullsweep_after, 10}])}. + {ok, proc_lib:spawn_opt(?MODULE, init, [[Conn, Env]], + [{spawn_opt, [link | ?FULLSWEEP_OPTS]}])}. info(CPid) -> gen_server2:call(CPid, info). diff --git a/src/emqttd_session.erl b/src/emqttd_session.erl index 1c575b3ed..1ee57b575 100644 --- a/src/emqttd_session.erl +++ b/src/emqttd_session.erl @@ -171,7 +171,7 @@ -spec(start_link(boolean(), {mqtt_client_id(), mqtt_username()}, pid()) -> {ok, pid()} | {error, any()}). start_link(CleanSess, {ClientId, Username}, ClientPid) -> gen_server2:start_link(?MODULE, [CleanSess, {ClientId, Username}, ClientPid], - [{fullsweep_after, 10}]). %% Tune GC. + [{spawn_opt, ?FULLSWEEP_OPTS}]). %% Tune GC. %%-------------------------------------------------------------------- %% PubSub API diff --git a/src/emqttd_ws_client.erl b/src/emqttd_ws_client.erl index 4faa44624..1d181e1bb 100644 --- a/src/emqttd_ws_client.erl +++ b/src/emqttd_ws_client.erl @@ -26,6 +26,8 @@ -include("emqttd_protocol.hrl"). +-include("emqttd_internal.hrl"). + -import(proplists, [get_value/3]). %% API Exports @@ -60,7 +62,7 @@ %% @doc Start WebSocket Client. start_link(Env, WsPid, Req, ReplyChannel) -> gen_server2:start_link(?MODULE, [Env, WsPid, Req, ReplyChannel], - [{fullsweep_after, 10}]). %% Tune GC. + [{spawn_opt, ?FULLSWEEP_OPTS}]). %% Tune GC. info(CPid) -> gen_server2:call(CPid, info). From e972103f74f494419d0801b0a08a0b2185d8e807 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Thu, 23 Feb 2017 17:25:44 +0800 Subject: [PATCH 083/125] Fix the 'spawn_opt' options --- src/emqttd_client.erl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/emqttd_client.erl b/src/emqttd_client.erl index cd7ab45d0..f7571efb9 100644 --- a/src/emqttd_client.erl +++ b/src/emqttd_client.erl @@ -66,8 +66,7 @@ [esockd_net:format(State#client_state.peername) | Args])). start_link(Conn, Env) -> - {ok, proc_lib:spawn_opt(?MODULE, init, [[Conn, Env]], - [{spawn_opt, [link | ?FULLSWEEP_OPTS]}])}. + {ok, proc_lib:spawn_opt(?MODULE, init, [[Conn, Env]], [link | ?FULLSWEEP_OPTS])}. info(CPid) -> gen_server2:call(CPid, info). From 5ef4fce141d46f1f1937d979a74ffff0e2ec04da Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Thu, 23 Feb 2017 17:26:29 +0800 Subject: [PATCH 084/125] Handle the {ok, I} return --- src/emqttd_gc.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/emqttd_gc.erl b/src/emqttd_gc.erl index 339e895f3..9261e297e 100644 --- a/src/emqttd_gc.erl +++ b/src/emqttd_gc.erl @@ -25,8 +25,8 @@ -spec(conn_max_gc_count() -> integer()). conn_max_gc_count() -> case emqttd:env(conn_force_gc_count) of - undefined -> undefined; - I when I > 0 -> I + rand:uniform(I) + {ok, I} when I > 0 -> I + rand:uniform(I); + undefined -> undefined end. -spec(reset_conn_gc_count(pos_integer(), tuple()) -> tuple()). From 1e3675028873e744a3f07dc608e922f5c839603f Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Thu, 23 Feb 2017 17:40:50 +0800 Subject: [PATCH 085/125] Use emqttd_gc:reset_conn_gc_count/2 API --- src/emqttd_client.erl | 2 +- src/emqttd_session.erl | 2 +- src/emqttd_ws_client.erl | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/emqttd_client.erl b/src/emqttd_client.erl index f7571efb9..e3e68b8e4 100644 --- a/src/emqttd_client.erl +++ b/src/emqttd_client.erl @@ -149,7 +149,7 @@ prioritise_info(Msg, _Len, _State) -> case Msg of {redeliver, _} -> 5; _ -> 0 end. handle_pre_hibernate(State) -> - {hibernate, emit_stats(emqttd_gc:reset_conn_gc_count(State))}. + {hibernate, emqttd_gc:reset_conn_gc_count(#client_state.force_gc_count, emit_stats(State))}. handle_call(info, From, State = #client_state{proto_state = ProtoState}) -> ProtoInfo = emqttd_protocol:info(ProtoState), diff --git a/src/emqttd_session.erl b/src/emqttd_session.erl index 1ee57b575..288565a3a 100644 --- a/src/emqttd_session.erl +++ b/src/emqttd_session.erl @@ -341,7 +341,7 @@ prioritise_info(Msg, _Len, _State) -> end. handle_pre_hibernate(State) -> - {hibernate, emit_stats(emqttd_gc:reset_conn_gc_count(State))}. + {hibernate, emqttd_gc:reset_conn_gc_count(#state.force_gc_count, emit_stats(State))}. handle_call({publish, Msg = #mqtt_message{qos = ?QOS_2, pktid = PacketId}}, _From, State = #state{awaiting_rel = AwaitingRel, diff --git a/src/emqttd_ws_client.erl b/src/emqttd_ws_client.erl index 1d181e1bb..bde322dcf 100644 --- a/src/emqttd_ws_client.erl +++ b/src/emqttd_ws_client.erl @@ -112,8 +112,8 @@ prioritise_info(Msg, _Len, _State) -> case Msg of {redeliver, _} -> 5; _ -> 0 end. handle_pre_hibernate(State = #wsclient_state{ws_pid = WsPid}) -> - erlang:garbage_collect(WsPid),%%TODO: [{async, RequestId}]?? - {hibernate, emqttd_gc:reset_conn_gc_count(emit_stats(State))}. + erlang:garbage_collect(WsPid), + {hibernate, emqttd_gc:reset_conn_gc_count(#wsclient_state.force_gc_count, emit_stats(State))}. handle_call(info, From, State = #wsclient_state{peername = Peername, proto_state = ProtoState}) -> From edd99dc5ed3e9b9408d3e30670ab6c252c0cef80 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Thu, 23 Feb 2017 18:53:16 +0800 Subject: [PATCH 086/125] Disable the force GC if conn_force_gc_count = 0 --- etc/emq.conf | 2 +- src/emqttd_gc.erl | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/etc/emq.conf b/etc/emq.conf index 4c3ab73c9..5f43eff51 100644 --- a/etc/emq.conf +++ b/etc/emq.conf @@ -110,7 +110,7 @@ mqtt.max_packet_size = 64KB ## MQTT Connection ##-------------------------------------------------------------------- -## Force GC: pos_integer +## Force GC: integer. Value 0 disabled the Force GC. mqtt.conn.force_gc_count = 100 ##-------------------------------------------------------------------- diff --git a/src/emqttd_gc.erl b/src/emqttd_gc.erl index 9261e297e..04cdbf2d5 100644 --- a/src/emqttd_gc.erl +++ b/src/emqttd_gc.erl @@ -26,6 +26,7 @@ conn_max_gc_count() -> case emqttd:env(conn_force_gc_count) of {ok, I} when I > 0 -> I + rand:uniform(I); + {ok, I} when I =< 0 -> undefined; undefined -> undefined end. From 9309baf17cade72873b65ac1bc5eb22a9be467c9 Mon Sep 17 00:00:00 2001 From: Feng Date: Thu, 23 Feb 2017 22:59:26 +0800 Subject: [PATCH 087/125] Rename mqtt_msgid() type to mqtt_msg_id() --- include/emqttd.hrl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/emqttd.hrl b/include/emqttd.hrl index 3f4f22229..a65086858 100644 --- a/include/emqttd.hrl +++ b/include/emqttd.hrl @@ -58,6 +58,7 @@ %%-------------------------------------------------------------------- %% MQTT Subscription %%-------------------------------------------------------------------- + -record(mqtt_subscription, { subid :: binary() | atom(), topic :: binary(), @@ -104,7 +105,7 @@ %% MQTT Message %%-------------------------------------------------------------------- --type(mqtt_msgid() :: binary() | undefined). +-type(mqtt_msg_id() :: binary() | undefined). -type(mqtt_pktid() :: 1..16#ffff | undefined). @@ -112,7 +113,7 @@ -record(mqtt_message, { %% Global unique message ID - id :: mqtt_msgid(), + id :: mqtt_msg_id(), %% PacketId pktid :: mqtt_pktid(), %% ClientId and Username From 440011da9a6c503e074c676190bea1866b0951f4 Mon Sep 17 00:00:00 2001 From: Feng Date: Thu, 23 Feb 2017 23:00:55 +0800 Subject: [PATCH 088/125] Rename 'Pkt' to 'PktCnt', 'Msg' to 'MsgCnt' --- src/emqttd_protocol.erl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/emqttd_protocol.erl b/src/emqttd_protocol.erl index 4b3c3ca76..b87274455 100644 --- a/src/emqttd_protocol.erl +++ b/src/emqttd_protocol.erl @@ -334,12 +334,12 @@ inc_stats(_Direct, _Type, Stats = #proto_stats{enable_stats = false}) -> Stats; inc_stats(recv, Type, Stats) -> - #proto_stats{recv_pkt = Pkt, recv_msg = Msg} = Stats, - inc_stats(Type, #proto_stats.recv_pkt, Pkt, #proto_stats.recv_msg, Msg, Stats); + #proto_stats{recv_pkt = PktCnt, recv_msg = MsgCnt} = Stats, + inc_stats(Type, #proto_stats.recv_pkt, PktCnt, #proto_stats.recv_msg, MsgCnt, Stats); inc_stats(send, Type, Stats) -> - #proto_stats{send_pkt = Pkt, send_msg = Msg} = Stats, - inc_stats(Type, #proto_stats.send_pkt, Pkt, #proto_stats.send_msg, Msg, Stats). + #proto_stats{send_pkt = PktCnt, send_msg = MsgCnt} = Stats, + inc_stats(Type, #proto_stats.send_pkt, PktCnt, #proto_stats.send_msg, MsgCnt, Stats). inc_stats(Type, PktPos, PktCnt, MsgPos, MsgCnt, Stats) -> Stats1 = setelement(PktPos, Stats, PktCnt + 1), From 1e91c0e220b4ee84e4dc98a152963f24fe8cc882 Mon Sep 17 00:00:00 2001 From: Feng Date: Thu, 23 Feb 2017 23:01:20 +0800 Subject: [PATCH 089/125] Format code --- src/emqttd_bridge.erl | 4 ++-- src/emqttd_pubsub.erl | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/emqttd_bridge.erl b/src/emqttd_bridge.erl index 7b3015f6d..a1dd34bcc 100644 --- a/src/emqttd_bridge.erl +++ b/src/emqttd_bridge.erl @@ -79,8 +79,8 @@ init([Pool, Id, Node, Topic, Options]) -> MQueue = emqttd_mqueue:new(qname(Node, Topic), [{max_len, State#state.max_queue_len}], emqttd_alarm:alarm_fun()), - {ok, State#state{pool = Pool, id = Id, mqueue = MQueue}, hibernate, - {backoff, 1000, 1000, 10000}}; + {ok, State#state{pool = Pool, id = Id, mqueue = MQueue}, + hibernate, {backoff, 1000, 1000, 10000}}; false -> {stop, {cannot_connect_node, Node}} end. diff --git a/src/emqttd_pubsub.erl b/src/emqttd_pubsub.erl index 393e4b788..d976618cd 100644 --- a/src/emqttd_pubsub.erl +++ b/src/emqttd_pubsub.erl @@ -164,8 +164,8 @@ pick(Subscriber) -> init([Pool, Id, Env]) -> ?GPROC_POOL(join, Pool, Id), - {ok, #state{pool = Pool, id = Id, env = Env}, hibernate, - {backoff, 2000, 2000, 20000}}. + {ok, #state{pool = Pool, id = Id, env = Env}, + hibernate, {backoff, 2000, 2000, 20000}}. handle_call({subscribe, Topic, Subscriber, Options}, _From, State) -> add_subscriber(Topic, Subscriber, Options), From 53572b7d376a70b28ce61e7a91328a51b6fb0afb Mon Sep 17 00:00:00 2001 From: Feng Date: Fri, 24 Feb 2017 20:38:35 +0800 Subject: [PATCH 090/125] Fix the comments of mqtt.session.* configurations --- etc/emq.conf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/etc/emq.conf b/etc/emq.conf index 5f43eff51..196ea99f3 100644 --- a/etc/emq.conf +++ b/etc/emq.conf @@ -130,14 +130,14 @@ mqtt.client.enable_stats = off ## Upgrade QoS? mqtt.session.upgrade_qos = off -## Max number of QoS 1 and 2 messages that can be “inflight” at one time. +## Max Size of the Inflight Window for QoS1 and QoS2 messages ## 0 means no limit mqtt.session.max_inflight = 32 ## Retry Interval for redelivering QoS1/2 messages. mqtt.session.retry_interval = 20s -## Max Packets that Awaiting PUBREL, 0 means no limit +## Client -> Broker: Max Packets Awaiting PUBREL, 0 means no limit mqtt.session.max_awaiting_rel = 100 ## Awaiting PUBREL Timeout From a71a06130591c77d0bc1d695281f7168d8559d16 Mon Sep 17 00:00:00 2001 From: J Phani Mahesh Date: Mon, 27 Feb 2017 14:01:20 +0530 Subject: [PATCH 091/125] add app.src,rebar.lock, ignores for rebar3 Having src/emqttd.app.src allows rebar family of build tools (rebar2, and rebar3) to be used to compile emqttd. This is important to allow plugins to be written using rebar3, to lock dependencies. rebar.lock contains references to commits or hashes of dependencies and is used by rebar3 to allow repeatable builds. This is important since without it, updates to dependencies are silently pulled and it is impractical to replicate a build properly. However, this adds an additional maintenance effort. Periodically, the lock file must be updated using rebar3 upgrade, and app.src must be updated when adding new dependencies. Note that erlang.mk does not respect dependency locking currently, and does not benefit from rebar.lock. _build is used by rebar3 and mix for storing built artifacts. --- .gitignore | 3 +++ rebar.lock | 32 ++++++++++++++++++++++++++++++++ src/emqttd.app.src | 8 ++++++++ 3 files changed, 43 insertions(+) create mode 100644 rebar.lock create mode 100644 src/emqttd.app.src diff --git a/.gitignore b/.gitignore index 8bbf0a28b..b78d533e5 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,6 @@ ct.coverdata emqttd.iml _rel/ data/ +_build +.rebar3 +rebar3.crashdump diff --git a/rebar.lock b/rebar.lock new file mode 100644 index 000000000..e5d182bad --- /dev/null +++ b/rebar.lock @@ -0,0 +1,32 @@ +[{<<"esockd">>, + {git,"https://github.com/emqtt/esockd", + {ref,"6ef597f16ce242fe37ae019d6ff5214f7a784c80"}}, + 0}, + {<<"gen_logger">>, + {git,"https://github.com/emqtt/gen_logger.git", + {ref,"f6e9f2f373d99f41ffe0579ab5a5f3b19472c9c5"}}, + 1}, + {<<"goldrush">>, + {git,"https://github.com/basho/goldrush.git", + {ref,"8f1b715d36b650ec1e1f5612c00e28af6ab0de82"}}, + 1}, + {<<"gproc">>, + {git,"https://github.com/uwiger/gproc", + {ref,"01c8fbfdd5e4701e8e4b57b0c8279872f9574b0b"}}, + 0}, + {<<"lager">>, + {git,"https://github.com/basho/lager", + {ref,"81eaef0ce98fdbf64ab95665e3bc2ec4b24c7dac"}}, + 0}, + {<<"lager_syslog">>, + {git,"https://github.com/basho/lager_syslog", + {ref,"126dd0284fcac9b01613189a82facf8d803411a2"}}, + 0}, + {<<"mochiweb">>, + {git,"https://github.com/emqtt/mochiweb", + {ref,"af27c0c90bf4c1bfeae0290e4c541264b69f7168"}}, + 0}, + {<<"syslog">>, + {git,"git://github.com/Vagabond/erlang-syslog", + {ref,"0e4f0e95c361af298c5d1d17ceccfa831efc036d"}}, + 1}]. diff --git a/src/emqttd.app.src b/src/emqttd.app.src new file mode 100644 index 000000000..c86af2a0a --- /dev/null +++ b/src/emqttd.app.src @@ -0,0 +1,8 @@ +{application, emqttd, [ + {description, "Erlang MQTT Broker"}, + {vsn, "2.1.0"}, + {modules, []}, + {registered, [emqttd_sup]}, + {applications, [kernel,stdlib,gproc,lager,esockd,mochiweb,lager_syslog]}, + {mod, {emqttd_app, []}} +]}. From 93719a13f3d5d6946b4f6894d78ad487be01ad59 Mon Sep 17 00:00:00 2001 From: buxizhizhoum Date: Fri, 3 Mar 2017 22:05:26 +0800 Subject: [PATCH 092/125] Edit the note of tick timer --- src/emqttd_broker.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/emqttd_broker.erl b/src/emqttd_broker.erl index ce2e4210c..eba1a6ac8 100644 --- a/src/emqttd_broker.erl +++ b/src/emqttd_broker.erl @@ -106,7 +106,7 @@ start_tick(0, _Msg) -> start_tick(Interval, Msg) when Interval > 0 -> {ok, TRef} = timer:send_interval(Interval, Msg), TRef. -%% @doc Start tick timer +%% @doc Stop tick timer stop_tick(undefined) -> ok; stop_tick(TRef) -> From 4de8c0921e01fa4b5afb58213033cf9b47bd7592 Mon Sep 17 00:00:00 2001 From: huangdan Date: Sat, 4 Mar 2017 16:23:13 +0800 Subject: [PATCH 093/125] Add test case --- test/emqttd_SUITE.erl | 62 +++++++- test/emqttd_SUITE_data/emqttd.conf | 98 +++++++++---- test/emqttd_SUITE_data/emqttd.schema | 206 +++++++++++++++++++-------- 3 files changed, 272 insertions(+), 94 deletions(-) diff --git a/test/emqttd_SUITE.erl b/test/emqttd_SUITE.erl index d7af619df..e78d856b1 100644 --- a/test/emqttd_SUITE.erl +++ b/test/emqttd_SUITE.erl @@ -47,7 +47,8 @@ all() -> {group, http}, {group, cluster}, {group, alarms}, - {group, cli}]. + {group, cli}, + {group, cleanSession}]. groups() -> [{protocol, [sequence], @@ -103,7 +104,11 @@ groups() -> cli_bridges, cli_plugins, cli_listeners, - cli_vm]}]. + cli_vm]}, + {cleanSession, [sequence], + [cleanSession_validate, + cleanSession_validate1, + cleanSession_validate2]}]. init_per_suite(Config) -> application:start(lager), @@ -616,6 +621,59 @@ cli_vm(_) -> emqttd_cli:vm([]), emqttd_cli:vm(["ports"]). +cleanSession_validate(_) -> + {ok, C1} = emqttc:start_link([{host, "localhost"}, + {port, 1883}, + {client_id, <<"c1">>}, + {clean_sess, false}]), + timer:sleep(10), + emqttc:subscribe(C1, <<"topic">>, qos0), + ok = emqttd_cli:sessions(["list", "persistent"]), + emqttc:disconnect(C1), + {ok, Pub} = emqttc:start_link([{host, "localhost"}, + {port, 1883}, + {client_id, <<"pub">>}]), + + emqttc:publish(Pub, <<"topic">>, <<"m1">>, [{qos, 0}]), + timer:sleep(10), + {ok, C11} = emqttc:start_link([{host, "localhost"}, + {port, 1883}, + {client_id, <<"c1">>}, + {clean_sess, false}]), + timer:sleep(100), + Metrics = emqttd_metrics:all(), + ct:log("Metrics:~p~n", [Metrics]), + ?assertEqual(1, proplists:get_value('messages/qos0/sent', Metrics)), + ?assertEqual(1, proplists:get_value('messages/qos0/received', Metrics)), + emqttc:disconnect(Pub), + emqttc:disconnect(C11). + +cleanSession_validate1(_) -> + {ok, C1} = emqttc:start_link([{host, "localhost"}, + {port, 1883}, + {client_id, <<"c1">>}, + {clean_sess, true}]), + timer:sleep(10), + emqttc:subscribe(C1, <<"topic">>, qos1), + ok = emqttd_cli:sessions(["list", "transient"]), + emqttc:disconnect(C1), + {ok, Pub} = emqttc:start_link([{host, "localhost"}, + {port, 1883}, + {client_id, <<"pub">>}]), + + emqttc:publish(Pub, <<"topic">>, <<"m1">>, [{qos, 1}]), + timer:sleep(10), + {ok, C11} = emqttc:start_link([{host, "localhost"}, + {port, 1883}, + {client_id, <<"c1">>}, + {clean_sess, false}]), + timer:sleep(100), + Metrics = emqttd_metrics:all(), + ?assertEqual(0, proplists:get_value('messages/qos1/sent', Metrics)), + ?assertEqual(1, proplists:get_value('messages/qos1/received', Metrics)), + emqttc:disconnect(Pub), + emqttc:disconnect(C11). + ensure_ok(ok) -> ok; ensure_ok({error, {already_started, _}}) -> ok. diff --git a/test/emqttd_SUITE_data/emqttd.conf b/test/emqttd_SUITE_data/emqttd.conf index 4aaa60eff..a15aeada9 100644 --- a/test/emqttd_SUITE_data/emqttd.conf +++ b/test/emqttd_SUITE_data/emqttd.conf @@ -1,3 +1,8 @@ + +##=================================================================== +## EMQ Configuration R2.1 +##=================================================================== + ##-------------------------------------------------------------------- ## Node Args ##-------------------------------------------------------------------- @@ -11,6 +16,11 @@ node.cookie = emq_dist_cookie ## SMP support: enable, auto, disable node.smp = auto +## vm.args: -heart +## Heartbeat monitoring of an Erlang runtime system +## Value should be 'on' or comment the line +## node.heartbeat = on + ## Enable kernel poll node.kernel_poll = on @@ -40,21 +50,30 @@ node.crash_dump = log/crash.dump node.dist_net_ticktime = 60 ## Distributed node port range -## node.dist_listen_min = 6000 -## node.dist_listen_max = 6999 +## node.dist_listen_min = 6369 +## node.dist_listen_max = 6369 ##-------------------------------------------------------------------- ## Log ##-------------------------------------------------------------------- +## Set the log dir +log.dir = {{ platform_log_dir }} + ## Console log. Enum: off, file, console, both log.console = console +## Syslog. Enum: on, off +log.syslog = on + +## syslog level. Enum: debug, info, notice, warning, error, critical, alert, emergency +log.syslog.level = error + ## Console log level. Enum: debug, info, notice, warning, error, critical, alert, emergency log.console.level = error ## Console log file -## log.console.file = log/console.log +## log.console.file = {{ platform_log_dir }}/console.log ## Error log file log.error.file = log/error.log @@ -64,6 +83,19 @@ log.crash = on log.crash.file = log/crash.log +##-------------------------------------------------------------------- +## Allow Anonymous and Default ACL +##-------------------------------------------------------------------- + +## Allow Anonymous authentication +mqtt.allow_anonymous = true + +## Default ACL File +mqtt.acl_file = etc/acl.conf + +## Cache ACL for PUBLISH +mqtt.cache_acl = true + ##-------------------------------------------------------------------- ## MQTT Protocol ##-------------------------------------------------------------------- @@ -74,34 +106,38 @@ mqtt.max_clientid_len = 1024 ## Max Packet Size Allowed, 64K by default. mqtt.max_packet_size = 64KB +##-------------------------------------------------------------------- +## MQTT Client +##-------------------------------------------------------------------- + ## Client Idle Timeout (Second) -mqtt.client_idle_timeout = 30 +mqtt.client.idle_timeout = 30s -## Allow Anonymous authentication -mqtt.allow_anonymous = true - -## Default ACL File -mqtt.acl_file = etc/acl.conf +## Enable client Stats: seconds or off +mqtt.client.enable_stats = off ##-------------------------------------------------------------------- ## MQTT Session ##-------------------------------------------------------------------- +## Upgrade QoS? +mqtt.session.upgrade_qos = off + ## Max number of QoS 1 and 2 messages that can be “inflight” at one time. ## 0 means no limit -mqtt.session.max_inflight = 100 +mqtt.session.max_inflight = 32 -## Retry interval for redelivering QoS1/2 messages. -mqtt.session.retry_interval = 60 - -## Awaiting PUBREL Timeout -mqtt.session.await_rel_timeout = 20 +## Retry Interval for redelivering QoS1/2 messages. +mqtt.session.retry_interval = 20s ## Max Packets that Awaiting PUBREL, 0 means no limit -mqtt.session.max_awaiting_rel = 0 +mqtt.session.max_awaiting_rel = 100 -## Statistics Collection Interval(seconds) -mqtt.session.collect_interval = 0 +## Awaiting PUBREL Timeout +mqtt.session.await_rel_timeout = 20s + +## Enable Statistics at the Interval(seconds) +mqtt.session.enable_stats = off ## Expired after 1 day: ## w - week @@ -109,7 +145,7 @@ mqtt.session.collect_interval = 0 ## h - hour ## m - minute ## s - second -mqtt.session.expired_after = 1d +mqtt.session.expiry_interval = 2h ##-------------------------------------------------------------------- ## MQTT Queue @@ -204,12 +240,13 @@ mqtt.listener.ssl.max_clients = 512 ## Rate Limit. Format is 'burst,rate', Unit is KB/Sec ## mqtt.listener.ssl.rate_limit = 100,10 -## Configuring SSL Options -## See http://erlang.org/doc/man/ssl.html -mqtt.listener.ssl.handshake_timeout = 15 +## Configuring SSL Options. See http://erlang.org/doc/man/ssl.html +### TLS only for POODLE attack +mqtt.listener.ssl.tls_versions = tlsv1.2,tlsv1.1,tlsv1 +mqtt.listener.ssl.handshake_timeout = 15s mqtt.listener.ssl.keyfile = certs/key.pem mqtt.listener.ssl.certfile = certs/cert.pem -## mqtt.listener.ssl.cacertfile = etc/certs/cacert.pem +## mqtt.listener.ssl.cacertfile = {{ platform_etc_dir }}/certs/cacert.pem ## mqtt.listener.ssl.verify = verify_peer ## mqtt.listener.ssl.fail_if_no_peer_cert = true @@ -219,13 +256,14 @@ mqtt.listener.http.acceptors = 4 mqtt.listener.http.max_clients = 64 ## HTTP(SSL) Listener -## mqtt.listener.https = 8084 -## mqtt.listener.https.acceptors = 4 -## mqtt.listener.https.max_clients = 64 -## mqtt.listener.https.handshake_timeout = 15 -## mqtt.listener.https.certfile = etc/certs/cert.pem -## mqtt.listener.https.keyfile = etc/certs/key.pem -## mqtt.listener.https.cacertfile = etc/certs/cacert.pem +mqtt.listener.https = 8084 +mqtt.listener.https.acceptors = 4 +mqtt.listener.https.max_clients = 64 +mqtt.listener.https.handshake_timeout = 15 +mqtt.listener.https.keyfile = certs/key.pem +mqtt.listener.https.certfile = certs/cert.pem +## mqtt.listener.https.cacertfile = {{ platform_etc_dir }}/certs/cacert.pem + ## mqtt.listener.https.verify = verify_peer ## mqtt.listener.https.fail_if_no_peer_cert = true diff --git a/test/emqttd_SUITE_data/emqttd.schema b/test/emqttd_SUITE_data/emqttd.schema index 2c73482ed..156bc4874 100644 --- a/test/emqttd_SUITE_data/emqttd.schema +++ b/test/emqttd_SUITE_data/emqttd.schema @@ -22,6 +22,19 @@ hidden ]}. +%% @doc http://erlang.org/doc/man/heart.html +{mapping, "node.heartbeat", "vm_args.-heart", [ + {datatype, flag}, + hidden +]}. + +{translation, "vm_args.-heart", fun(Conf) -> + case cuttlefish:conf_get("node.heartbeat", Conf) of + true -> ""; + false -> cuttlefish:invalid("should be 'on' or comment the line!") + end +end}. + %% @doc Enable Kernel Poll {mapping, "node.kernel_poll", "vm_args.+K", [ {default, on}, @@ -135,8 +148,13 @@ %% Log %%-------------------------------------------------------------------- +{mapping, "log.dir", "lager.log_dir", [ + {default, "log"}, + {datatype, string} +]}. + {mapping, "log.console", "lager.handlers", [ - {default, file }, + {default, file}, {datatype, {enum, [off, file, console, both]}} ]}. @@ -155,6 +173,26 @@ {datatype, file} ]}. +{mapping, "log.syslog", "lager.handlers", [ + {default, off}, + {datatype, flag} +]}. + +{mapping, "log.syslog.identity", "lager.handlers", [ + {default, "emq"}, + {datatype, string} +]}. + +{mapping, "log.syslog.facility", "lager.handlers", [ + {default, local0}, + {datatype, {enum, [daemon, local0, local1, local2, local3, local4, local5, local6, local7]}} +]}. + +{mapping, "log.syslog.level", "lager.handlers", [ + {default, err}, + {datatype, {enum, [debug, info, notice, warning, error, critical, alert, emergency]}} +]}. + {mapping, "log.error.redirect", "lager.error_logger_redirect", [ {default, on}, {datatype, flag}, @@ -196,7 +234,16 @@ both -> [ConsoleHandler, ConsoleFileHandler]; _ -> [] end, - ConsoleHandlers ++ ErrorHandler + + SyslogHandler = case cuttlefish:conf_get("log.syslog", Conf) of + false -> []; + true -> [{lager_syslog_backend, + [cuttlefish:conf_get("log.syslog.identity", Conf), + cuttlefish:conf_get("log.syslog.facility", Conf), + cuttlefish:conf_get("log.syslog.level", Conf)]}] + end, + + ConsoleHandlers ++ ErrorHandler ++ SyslogHandler end }. @@ -226,6 +273,28 @@ hidden ]}. +%%-------------------------------------------------------------------- +%% Allow Anonymous and Default ACL +%%-------------------------------------------------------------------- + +%% @doc Allow Anonymous +{mapping, "mqtt.allow_anonymous", "emqttd.allow_anonymous", [ + {default, false}, + {datatype, {enum, [true, false]}} +]}. + +%% @doc Default ACL File +{mapping, "mqtt.acl_file", "emqttd.acl_file", [ + {datatype, string}, + hidden +]}. + +%% @doc Cache ACL for PUBLISH +{mapping, "mqtt.cache_acl", "emqttd.cache_acl", [ + {default, true}, + {datatype, {enum, [true, false]}} +]}. + %%-------------------------------------------------------------------- %% MQTT Protocol %%-------------------------------------------------------------------- @@ -242,35 +311,42 @@ {datatype, bytesize} ]}. -%% @doc Client Idle Timeout. -{mapping, "mqtt.client_idle_timeout", "emqttd.protocol", [ - {default, 30}, - {datatype, integer} -]}. - {translation, "emqttd.protocol", fun(Conf) -> - [{max_clientid_len, cuttlefish:conf_get("mqtt.max_clientid_len", Conf)}, - {max_packet_size, cuttlefish:conf_get("mqtt.max_packet_size", Conf)}, - {client_idle_timeout, cuttlefish:conf_get("mqtt.client_idle_timeout", Conf)}] + [{max_clientid_len, cuttlefish:conf_get("mqtt.max_clientid_len", Conf)}, + {max_packet_size, cuttlefish:conf_get("mqtt.max_packet_size", Conf)}] end}. -%% @doc Allow Anonymous -{mapping, "mqtt.allow_anonymous", "emqttd.allow_anonymous", [ - {default, false}, - {datatype, {enum, [true, false]}}, - hidden +%%-------------------------------------------------------------------- +%% MQTT Client +%%-------------------------------------------------------------------- + +%% @doc Client Idle Timeout. +{mapping, "mqtt.client.idle_timeout", "emqttd.client", [ + {default, "30s"}, + {datatype, {duration, ms}} ]}. -%% @doc Default ACL File -{mapping, "mqtt.acl_file", "emqttd.acl_file", [ - {datatype, string}, - hidden +%% @doc Enable Stats of Client. +{mapping, "mqtt.client.enable_stats", "emqttd.client", [ + {default, off}, + {datatype, [{duration, ms}, flag]} ]}. +%% @doc Client +{translation, "emqttd.client", fun(Conf) -> + [{client_idle_timeout, cuttlefish:conf_get("mqtt.client.idle_timeout", Conf)}, + {client_enable_stats, cuttlefish:conf_get("mqtt.client.enable_stats", Conf)}] +end}. + %%-------------------------------------------------------------------- %% MQTT Session %%-------------------------------------------------------------------- +%% @doc Upgrade QoS? +{mapping, "mqtt.session.upgrade_qos", "emqttd.session", [ + {default, off}, + {datatype, flag} +]}. %% @doc Max number of QoS 1 and 2 messages that can be “inflight” at one time. %% 0 means no limit {mapping, "mqtt.session.max_inflight", "emqttd.session", [ @@ -278,17 +354,10 @@ end}. {datatype, integer} ]}. - %% @doc Retry interval for redelivering QoS1/2 messages. {mapping, "mqtt.session.retry_interval", "emqttd.session", [ - {default, 60}, - {datatype, integer} -]}. - -%% @doc Awaiting PUBREL Timeout -{mapping, "mqtt.session.await_rel_timeout", "emqttd.session", [ - {default, 30}, - {datatype, integer} + {default, "20s"}, + {datatype, {duration, ms}} ]}. %% @doc Max Packets that Awaiting PUBREL, 0 means no limit @@ -297,25 +366,32 @@ end}. {datatype, integer} ]}. -%% @doc Statistics Collection Interval(seconds) -{mapping, "mqtt.session.collect_interval", "emqttd.session", [ - {default, 0}, - {datatype, integer} +%% @doc Awaiting PUBREL Timeout +{mapping, "mqtt.session.await_rel_timeout", "emqttd.session", [ + {default, "20s"}, + {datatype, {duration, ms}} ]}. -%% @doc Session expired after... -{mapping, "mqtt.session.expired_after", "emqttd.session", [ - {default, "2d"}, - {datatype, {duration, s}} +%% @doc Enable Stats +{mapping, "mqtt.session.enable_stats", "emqttd.session", [ + {default, off}, + {datatype, [{duration, ms}, flag]} +]}. + +%% @doc Session Expiry Interval +{mapping, "mqtt.session.expiry_interval", "emqttd.session", [ + {default, "2h"}, + {datatype, {duration, ms}} ]}. {translation, "emqttd.session", fun(Conf) -> - [{max_inflight, cuttlefish:conf_get("mqtt.session.max_inflight", Conf)}, - {retry_interval, cuttlefish:conf_get("mqtt.session.retry_interval", Conf)}, + [{upgrade_qos, cuttlefish:conf_get("mqtt.session.upgrade_qos", Conf)}, + {max_inflight, cuttlefish:conf_get("mqtt.session.max_inflight", Conf)}, + {retry_interval, cuttlefish:conf_get("mqtt.session.retry_interval", Conf)}, + {max_awaiting_rel, cuttlefish:conf_get("mqtt.session.max_awaiting_rel", Conf)}, {await_rel_timeout, cuttlefish:conf_get("mqtt.session.await_rel_timeout", Conf)}, - {max_awaiting_rel, cuttlefish:conf_get("mqtt.session.max_awaiting_rel", Conf)}, - {collect_interval, cuttlefish:conf_get("mqtt.session.collect_interval", Conf)}, - {expired_after, cuttlefish:conf_get("mqtt.session.expired_after", Conf)}] + {enable_stats, cuttlefish:conf_get("mqtt.session.enable_stats", Conf)}, + {expiry_interval, cuttlefish:conf_get("mqtt.session.expiry_interval", Conf)}] end}. %%-------------------------------------------------------------------- @@ -331,28 +407,25 @@ end}. %% @doc Topic Priority: 0~255, Default is 0 {mapping, "mqtt.queue.priority", "emqttd.queue", [ {default, ""}, - {datatype, string}, - hidden + {datatype, string} ]}. %% @doc Max queue length. Enqueued messages when persistent client disconnected, or inflight window is full. {mapping, "mqtt.queue.max_length", "emqttd.queue", [ {default, infinity}, - {datatype, [atom, integer]} + {datatype, [integer, {atom, infinity}]} ]}. %% @doc Low-water mark of queued messages {mapping, "mqtt.queue.low_watermark", "emqttd.queue", [ {default, "20%"}, - {datatype, string}, - hidden + {datatype, string} ]}. %% @doc High-water mark of queued messages {mapping, "mqtt.queue.high_watermark", "emqttd.queue", [ {default, "60%"}, - {datatype, string}, - hidden + {datatype, string} ]}. %% @doc Queue Qos0 messages? @@ -405,8 +478,7 @@ end}. {mapping, "mqtt.pubsub.async", "emqttd.pubsub", [ {default, true}, - {datatype, {enum, [true, false]}}, - hidden + {datatype, {enum, [true, false]}} ]}. {translation, "emqttd.pubsub", fun(Conf) -> @@ -451,7 +523,7 @@ end}. %%-------------------------------------------------------------------- {mapping, "mqtt.listener.tcp", "emqttd.listeners", [ - {default, 1883}, + %% {default, 1883}, {datatype, [integer, ip]} ]}. @@ -467,8 +539,7 @@ end}. {mapping, "mqtt.listener.tcp.rate_limit", "emqttd.listeners", [ {default, undefined}, - {datatype, string}, - hidden + {datatype, string} ]}. {mapping, "mqtt.listener.tcp.backlog", "emqttd.listeners", [ @@ -497,7 +568,7 @@ end}. ]}. {mapping, "mqtt.listener.ssl", "emqttd.listeners", [ - {default, 8883}, + %% {default, 8883}, {datatype, [integer, ip]} ]}. @@ -515,9 +586,13 @@ end}. {datatype, string} ]}. +{mapping, "mqtt.listener.ssl.tls_versions", "emqttd.listeners", [ + {datatype, string} +]}. + {mapping, "mqtt.listener.ssl.handshake_timeout", "emqttd.listeners", [ - {default, 15}, - {datatype, integer} + {default, "15s"}, + {datatype, {duration, ms}} ]}. {mapping, "mqtt.listener.ssl.keyfile", "emqttd.listeners", [ @@ -541,7 +616,7 @@ end}. ]}. {mapping, "mqtt.listener.http", "emqttd.listeners", [ - {default, 8883}, + %% {default, 8083}, {datatype, [integer, ip]} ]}. @@ -556,9 +631,8 @@ end}. ]}. {mapping, "mqtt.listener.https", "emqttd.listeners", [ - {default, undefined}, - {datatype, [integer, ip]}, - hidden + %%{default, 8084}, + {datatype, [integer, ip]} ]}. {mapping, "mqtt.listener.https.acceptors", "emqttd.listeners", [ @@ -610,8 +684,16 @@ end}. {buffer, cuttlefish:conf_get(Prefix ++ ".buffer", Conf, undefined)}, {nodelay, cuttlefish:conf_get(Prefix ++ ".nodelay", Conf, true)}]) end, + + SplitFun = fun(undefined) -> undefined; (S) -> string:tokens(S, ",") end, + SslOpts = fun(Prefix) -> - Filter([{handshake_timeout, cuttlefish:conf_get(Prefix ++ ".handshake_timeout", Conf) * 1000}, + Versions = case SplitFun(cuttlefish:conf_get(Prefix ++ ".tls_versions", Conf, undefined)) of + undefined -> undefined; + L -> [list_to_atom(V) || V <- L] + end, + Filter([{versions, Versions}, + {handshake_timeout, cuttlefish:conf_get(Prefix ++ ".handshake_timeout", Conf), undefined}, {keyfile, cuttlefish:conf_get(Prefix ++ ".keyfile", Conf, undefined)}, {certfile, cuttlefish:conf_get(Prefix ++ ".certfile", Conf, undefined)}, {cacertfile, cuttlefish:conf_get(Prefix ++ ".cacertfile", Conf, undefined)}, From 3d0bc15e93cfb7ed943472a2335a9de6bde3d55c Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Mon, 6 Mar 2017 18:06:15 +0800 Subject: [PATCH 094/125] Use the '?assertEqual' macro and update Copyright info --- test/emqttd_SUITE.erl | 2 +- test/emqttd_access_SUITE.erl | 2 +- test/emqttd_acl_test_mod.erl | 2 +- test/emqttd_auth_anonymous_test_mod.erl | 2 +- test/emqttd_auth_dashboard.erl | 2 +- test/emqttd_inflight_SUITE.erl | 1 + test/emqttd_lib_SUITE.erl | 2 +- test/emqttd_mod_SUITE.erl | 2 +- test/emqttd_mqueue_SUITE.erl | 2 +- test/emqttd_net_SUITE.erl | 2 +- test/emqttd_protocol_SUITE.erl | 51 ++++++++++++------------- test/emqttd_topic_SUITE.erl | 2 +- test/emqttd_trie_SUITE.erl | 2 +- test/emqttd_vm_SUITE.erl | 2 +- 14 files changed, 38 insertions(+), 38 deletions(-) diff --git a/test/emqttd_SUITE.erl b/test/emqttd_SUITE.erl index afa1a1f06..0ffef33ec 100644 --- a/test/emqttd_SUITE.erl +++ b/test/emqttd_SUITE.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/test/emqttd_access_SUITE.erl b/test/emqttd_access_SUITE.erl index 5ab7e992b..762ae6f40 100644 --- a/test/emqttd_access_SUITE.erl +++ b/test/emqttd_access_SUITE.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/test/emqttd_acl_test_mod.erl b/test/emqttd_acl_test_mod.erl index 196337fa4..08f1f9c94 100644 --- a/test/emqttd_acl_test_mod.erl +++ b/test/emqttd_acl_test_mod.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/test/emqttd_auth_anonymous_test_mod.erl b/test/emqttd_auth_anonymous_test_mod.erl index 8e93be0bc..be6a14bf8 100644 --- a/test/emqttd_auth_anonymous_test_mod.erl +++ b/test/emqttd_auth_anonymous_test_mod.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/test/emqttd_auth_dashboard.erl b/test/emqttd_auth_dashboard.erl index 0e509c08a..49f54c377 100644 --- a/test/emqttd_auth_dashboard.erl +++ b/test/emqttd_auth_dashboard.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/test/emqttd_inflight_SUITE.erl b/test/emqttd_inflight_SUITE.erl index 5a87d056d..de5391f1a 100644 --- a/test/emqttd_inflight_SUITE.erl +++ b/test/emqttd_inflight_SUITE.erl @@ -48,3 +48,4 @@ t_is_full(_) -> t_is_empty(_) -> Inflight = ((emqttd_inflight:new(1)):insert(k, v1)), ?assertNot(Inflight:is_empty()). + diff --git a/test/emqttd_lib_SUITE.erl b/test/emqttd_lib_SUITE.erl index c16858785..344e185d0 100644 --- a/test/emqttd_lib_SUITE.erl +++ b/test/emqttd_lib_SUITE.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/test/emqttd_mod_SUITE.erl b/test/emqttd_mod_SUITE.erl index a258eabe0..1fcf455d0 100644 --- a/test/emqttd_mod_SUITE.erl +++ b/test/emqttd_mod_SUITE.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2016-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/test/emqttd_mqueue_SUITE.erl b/test/emqttd_mqueue_SUITE.erl index eaa0ecc68..e56b08398 100644 --- a/test/emqttd_mqueue_SUITE.erl +++ b/test/emqttd_mqueue_SUITE.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/test/emqttd_net_SUITE.erl b/test/emqttd_net_SUITE.erl index 7d70f4291..e3ae0700c 100644 --- a/test/emqttd_net_SUITE.erl +++ b/test/emqttd_net_SUITE.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2016-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/test/emqttd_protocol_SUITE.erl b/test/emqttd_protocol_SUITE.erl index 027d550a0..1ac688650 100644 --- a/test/emqttd_protocol_SUITE.erl +++ b/test/emqttd_protocol_SUITE.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -260,7 +260,7 @@ serialize_connect(_) -> serialize_connack(_) -> ConnAck = #mqtt_packet{header = #mqtt_packet_header{type = ?CONNACK}, variable = #mqtt_packet_connack{ack_flags = 0, return_code = 0}}, - <<32,2,0,0>> = iolist_to_binary(serialize(ConnAck)). + ?assertEqual(<<32,2,0,0>>, iolist_to_binary(serialize(ConnAck))). serialize_publish(_) -> serialize(?PUBLISH_PACKET(?QOS_0, <<"Topic">>, undefined, <<"Payload">>)), @@ -303,20 +303,20 @@ long_payload() -> %%-------------------------------------------------------------------- packet_proto_name(_) -> - <<"MQIsdp">> = emqttd_packet:protocol_name(3), - <<"MQTT">> = emqttd_packet:protocol_name(4). + ?assertEqual(<<"MQIsdp">>, emqttd_packet:protocol_name(3)), + ?assertEqual(<<"MQTT">>, emqttd_packet:protocol_name(4)). packet_type_name(_) -> - 'CONNECT' = emqttd_packet:type_name(?CONNECT), - 'UNSUBSCRIBE' = emqttd_packet:type_name(?UNSUBSCRIBE). + ?assertEqual('CONNECT', emqttd_packet:type_name(?CONNECT)), + ?assertEqual('UNSUBSCRIBE', emqttd_packet:type_name(?UNSUBSCRIBE)). packet_connack_name(_) -> - 'CONNACK_ACCEPT' = emqttd_packet:connack_name(?CONNACK_ACCEPT), - 'CONNACK_PROTO_VER' = emqttd_packet:connack_name(?CONNACK_PROTO_VER), - 'CONNACK_INVALID_ID' = emqttd_packet:connack_name(?CONNACK_INVALID_ID), - 'CONNACK_SERVER' = emqttd_packet:connack_name(?CONNACK_SERVER), - 'CONNACK_CREDENTIALS' = emqttd_packet:connack_name(?CONNACK_CREDENTIALS), - 'CONNACK_AUTH' = emqttd_packet:connack_name(?CONNACK_AUTH). + ?assertEqual('CONNACK_ACCEPT', emqttd_packet:connack_name(?CONNACK_ACCEPT)), + ?assertEqual('CONNACK_PROTO_VER', emqttd_packet:connack_name(?CONNACK_PROTO_VER)), + ?assertEqual('CONNACK_INVALID_ID', emqttd_packet:connack_name(?CONNACK_INVALID_ID)), + ?assertEqual('CONNACK_SERVER', emqttd_packet:connack_name(?CONNACK_SERVER)), + ?assertEqual('CONNACK_CREDENTIALS', emqttd_packet:connack_name(?CONNACK_CREDENTIALS)), + ?assertEqual('CONNACK_AUTH', emqttd_packet:connack_name(?CONNACK_AUTH)). packet_format(_) -> io:format("~s", [emqttd_packet:format(?CONNECT_PACKET(#mqtt_packet_connect{}))]), @@ -336,26 +336,25 @@ packet_format(_) -> message_make(_) -> Msg = emqttd_message:make(<<"clientid">>, <<"topic">>, <<"payload">>), - 0 = Msg#mqtt_message.qos, + ?assertEqual(0, Msg#mqtt_message.qos), Msg1 = emqttd_message:make(<<"clientid">>, qos2, <<"topic">>, <<"payload">>), - true = is_binary(Msg1#mqtt_message.id), - 2 = Msg1#mqtt_message.qos. + ?assert(is_binary(Msg1#mqtt_message.id)), + ?assertEqual(2, Msg1#mqtt_message.qos). message_from_packet(_) -> Msg = emqttd_message:from_packet(?PUBLISH_PACKET(1, <<"topic">>, 10, <<"payload">>)), - 1 = Msg#mqtt_message.qos, - 10 = Msg#mqtt_message.pktid, - <<"topic">> = Msg#mqtt_message.topic, - + ?assertEqual(1, Msg#mqtt_message.qos), + ?assertEqual(10, Msg#mqtt_message.packet_id), + ?assertEqual(<<"topic">>, Msg#mqtt_message.topic), WillMsg = emqttd_message:from_packet(#mqtt_packet_connect{will_flag = true, will_topic = <<"WillTopic">>, will_msg = <<"WillMsg">>}), - <<"WillTopic">> = WillMsg#mqtt_message.topic, - <<"WillMsg">> = WillMsg#mqtt_message.payload, + ?assertEqual(<<"WillTopic">>, WillMsg#mqtt_message.topic), + ?assertEqual(<<"WillMsg">>, WillMsg#mqtt_message.payload), Msg2 = emqttd_message:from_packet(<<"username">>, <<"clientid">>, ?PUBLISH_PACKET(1, <<"topic">>, 20, <<"payload">>)), - {<<"clientid">>, <<"username">>} = Msg2#mqtt_message.from, + ?assertEqual({<<"clientid">>, <<"username">>}, Msg2#mqtt_message.from), io:format("~s", [emqttd_message:format(Msg2)]). message_flag(_) -> @@ -363,13 +362,13 @@ message_flag(_) -> Msg2 = emqttd_message:from_packet(<<"clientid">>, Pkt), Msg3 = emqttd_message:set_flag(retain, Msg2), Msg4 = emqttd_message:set_flag(dup, Msg3), - true = Msg4#mqtt_message.dup, - true = Msg4#mqtt_message.retain, + ?assert(Msg4#mqtt_message.dup), + ?assert(Msg4#mqtt_message.retain), Msg5 = emqttd_message:set_flag(Msg4), Msg6 = emqttd_message:unset_flag(dup, Msg5), Msg7 = emqttd_message:unset_flag(retain, Msg6), - false = Msg7#mqtt_message.dup, - false = Msg7#mqtt_message.retain, + ?assertNot(Msg7#mqtt_message.dup), + ?assertNot(Msg7#mqtt_message.retain), emqttd_message:unset_flag(Msg7), emqttd_message:to_packet(Msg7). diff --git a/test/emqttd_topic_SUITE.erl b/test/emqttd_topic_SUITE.erl index f8be7df1a..a43875fba 100644 --- a/test/emqttd_topic_SUITE.erl +++ b/test/emqttd_topic_SUITE.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2016-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/test/emqttd_trie_SUITE.erl b/test/emqttd_trie_SUITE.erl index 37d247755..2394a902a 100644 --- a/test/emqttd_trie_SUITE.erl +++ b/test/emqttd_trie_SUITE.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/test/emqttd_vm_SUITE.erl b/test/emqttd_vm_SUITE.erl index ba56ef97f..ef0ac2946 100644 --- a/test/emqttd_vm_SUITE.erl +++ b/test/emqttd_vm_SUITE.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. From 98e15ebbf4e3799d7ef9c6126e9ea7eb4df11f26 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Mon, 6 Mar 2017 18:11:09 +0800 Subject: [PATCH 095/125] Prepare for MQTT/5.0 --- include/emqttd.hrl | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/include/emqttd.hrl b/include/emqttd.hrl index a65086858..a02c86129 100644 --- a/include/emqttd.hrl +++ b/include/emqttd.hrl @@ -22,9 +22,9 @@ -define(LICENSE_MESSAGE, "Licensed under the Apache License, Version 2.0"). --define(PROTOCOL_VERSION, "MQTT/3.1.1"). +-define(PROTOCOL_VERSION, "MQTT/5.0"). --define(ERTS_MINIMUM, "7.0"). +-define(ERTS_MINIMUM, "8.0"). %%-------------------------------------------------------------------- %% Sys/Queue/Share Topics' Prefix @@ -42,7 +42,7 @@ -type(pubsub() :: publish | subscribe). --define(PUBSUB(PS), (PS =:= publish orelse PS =:= subscribe)). +-define(PS(PS), (PS =:= publish orelse PS =:= subscribe)). %%-------------------------------------------------------------------- %% MQTT Topic @@ -172,7 +172,7 @@ severity :: warning | error | critical, title :: iolist() | binary(), summary :: iolist() | binary(), - timestamp :: erlang:timestamp() %% Timestamp + timestamp :: erlang:timestamp() }). -type(mqtt_alarm() :: #mqtt_alarm{}). @@ -186,8 +186,7 @@ -type(mqtt_plugin() :: #mqtt_plugin{}). %%-------------------------------------------------------------------- -%% MQTT CLI Command -%% For example: 'broker metrics' +%% MQTT CLI Command. For example: 'broker metrics' %%-------------------------------------------------------------------- -record(mqtt_cli, { name, action, args = [], opts = [], usage, descr }). From c3919a64e60582ed4a7b024fe1380fdd2ab7b476 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Mon, 6 Mar 2017 18:13:07 +0800 Subject: [PATCH 096/125] Format 'trie_node' record --- include/emqttd_trie.hrl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/emqttd_trie.hrl b/include/emqttd_trie.hrl index eb4e1390d..c5b9d03c7 100644 --- a/include/emqttd_trie.hrl +++ b/include/emqttd_trie.hrl @@ -17,10 +17,10 @@ -type(trie_node_id() :: binary() | atom()). -record(trie_node, - { node_id :: trie_node_id(), - edge_count = 0 :: non_neg_integer(), - topic :: binary() | undefined, - flags :: [retained | static] + { node_id :: trie_node_id(), + edge_count = 0 :: non_neg_integer(), + topic :: binary() | undefined, + flags :: [retained | static] }). -record(trie_edge, From 35e08df735a9d78af2fe303bfce55da186d66870 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Mon, 6 Mar 2017 18:17:07 +0800 Subject: [PATCH 097/125] Change the default QoS of will message to QOS_1 --- include/emqttd_protocol.hrl | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/include/emqttd_protocol.hrl b/include/emqttd_protocol.hrl index 6181b9a65..9d9cec714 100644 --- a/include/emqttd_protocol.hrl +++ b/include/emqttd_protocol.hrl @@ -80,6 +80,7 @@ %%-------------------------------------------------------------------- %% MQTT Control Packet Types %%-------------------------------------------------------------------- + -define(RESERVED, 0). %% Reserved -define(CONNECT, 1). %% Client request to connect to Server -define(CONNACK, 2). %% Server to Client: Connect acknowledgment @@ -94,7 +95,7 @@ -define(UNSUBACK, 11). %% Unsubscribe acknowledgment -define(PINGREQ, 12). %% PING request -define(PINGRESP, 13). %% PING response --define(DISCONNECT, 14). %% Client is disconnecting +-define(DISCONNECT, 14). %% Client or Server is disconnecting -define(AUTH, 15). %% Authentication exchange -define(TYPE_NAMES, [ @@ -146,11 +147,12 @@ %% MQTT Packet Fixed Header %%-------------------------------------------------------------------- --record(mqtt_packet_header, { - type = ?RESERVED :: mqtt_packet_type(), - dup = false :: boolean(), - qos = ?QOS_0 :: mqtt_qos(), - retain = false :: boolean()}). +-record(mqtt_packet_header, + { type = ?RESERVED :: mqtt_packet_type(), + dup = false :: boolean(), + qos = ?QOS_0 :: mqtt_qos(), + retain = false :: boolean() + }). %%-------------------------------------------------------------------- %% MQTT Packets @@ -165,7 +167,7 @@ proto_ver = ?MQTT_PROTO_V4 :: mqtt_vsn(), proto_name = <<"MQTT">> :: binary(), will_retain = false :: boolean(), - will_qos = ?QOS_0 :: mqtt_qos(), + will_qos = ?QOS_1 :: mqtt_qos(), will_flag = false :: boolean(), clean_sess = false :: boolean(), keep_alive = 60 :: non_neg_integer(), @@ -199,25 +201,25 @@ }). -record(mqtt_packet_suback, - { packet_id :: mqtt_packet_id(), - qos_table :: list(mqtt_qos() | 128) + { packet_id :: mqtt_packet_id(), + qos_table :: list(mqtt_qos() | 128) }). -record(mqtt_packet_unsuback, - { packet_id :: mqtt_packet_id() }). + { packet_id :: mqtt_packet_id() }). %%-------------------------------------------------------------------- %% MQTT Control Packet %%-------------------------------------------------------------------- -record(mqtt_packet, - { header :: #mqtt_packet_header{}, - variable :: #mqtt_packet_connect{} | #mqtt_packet_connack{} - | #mqtt_packet_publish{} | #mqtt_packet_puback{} - | #mqtt_packet_subscribe{} | #mqtt_packet_suback{} - | #mqtt_packet_unsubscribe{} | #mqtt_packet_unsuback{} - | mqtt_packet_id() | undefined, - payload :: binary() | undefined + { header :: #mqtt_packet_header{}, + variable :: #mqtt_packet_connect{} | #mqtt_packet_connack{} + | #mqtt_packet_publish{} | #mqtt_packet_puback{} + | #mqtt_packet_subscribe{} | #mqtt_packet_suback{} + | #mqtt_packet_unsubscribe{} | #mqtt_packet_unsuback{} + | mqtt_packet_id() | undefined, + payload :: binary() | undefined }). -type(mqtt_packet() :: #mqtt_packet{}). From 99c83dbe21138f20a4b969513dbacb6a4134afc0 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Mon, 6 Mar 2017 18:28:16 +0800 Subject: [PATCH 098/125] Add 'mqtt.listener.tcp.tune_buffer' config --- priv/emq.schema | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/priv/emq.schema b/priv/emq.schema index 92ca118d2..b5f40b893 100644 --- a/priv/emq.schema +++ b/priv/emq.schema @@ -132,14 +132,14 @@ end}. %% @doc http://www.erlang.org/doc/man/kernel_app.html {mapping, "node.dist_listen_min", "kernel.inet_dist_listen_min", [ - {commented, 6000}, + {commented, 6369}, {datatype, integer}, hidden ]}. %% @see node.dist_listen_min {mapping, "node.dist_listen_max", "kernel.inet_dist_listen_max", [ - {commented, 6999}, + {commented, 6369}, {datatype, integer}, hidden ]}. @@ -356,6 +356,7 @@ end}. {default, off}, {datatype, flag} ]}. + %% @doc Max number of QoS 1 and 2 messages that can be “inflight” at one time. %% 0 means no limit {mapping, "mqtt.session.max_inflight", "emqttd.session", [ @@ -571,6 +572,11 @@ end}. hidden ]}. +{mapping, "mqtt.listener.tcp.tune_buffer", "emqttd.listeners", [ + {default, off}, + {datatype, flag} +]}. + {mapping, "mqtt.listener.tcp.nodelay", "emqttd.listeners", [ {datatype, {enum, [true, false]}}, hidden @@ -684,8 +690,8 @@ end}. LisOpts = fun(Prefix) -> Filter([{acceptors, cuttlefish:conf_get(Prefix ++ ".acceptors", Conf)}, {max_clients, cuttlefish:conf_get(Prefix ++ ".max_clients", Conf)}, - {rate_limt, cuttlefish:conf_get(Prefix ++ ".rate_limit", Conf, undefined)}]) - end, + {tune_buffer, cuttlefish:conf_get(Prefix ++ ".tune_buffer", Conf, undefined)}]) + end, TcpOpts = fun(Prefix) -> Filter([{backlog, cuttlefish:conf_get(Prefix ++ ".backlog", Conf, undefined)}, {recbuf, cuttlefish:conf_get(Prefix ++ ".recbuf", Conf, undefined)}, From dae3d22bef2dd3eb6160b8aebefce0dfbfe1930d Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Mon, 6 Mar 2017 18:43:44 +0800 Subject: [PATCH 099/125] Remove the io:format line --- src/emqttd_sm.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/emqttd_sm.erl b/src/emqttd_sm.erl index 02dfdb879..bbba50d6a 100644 --- a/src/emqttd_sm.erl +++ b/src/emqttd_sm.erl @@ -107,7 +107,7 @@ dispatch(ClientId, Topic, Msg) -> try ets:lookup_element(mqtt_local_session, ClientId, 2) of Pid -> Pid ! {dispatch, Topic, Msg} catch - error:badarg -> io:format("Session Not Found: ~p~n", [ClientId]), ok %%TODO: How?? + error:badarg -> ok %%FIXME Later. end. call(SM, Req) -> From f2a818a4a4823e3720295a22adefa53e11349328 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Mon, 6 Mar 2017 18:46:09 +0800 Subject: [PATCH 100/125] Rename the PUBSUB macro to PS --- src/emqttd_access_control.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/emqttd_access_control.erl b/src/emqttd_access_control.erl index cc60a57f2..65d0c76f5 100644 --- a/src/emqttd_access_control.erl +++ b/src/emqttd_access_control.erl @@ -70,7 +70,7 @@ auth(Client, Password, [{Mod, State, _Seq} | Mods]) -> Client :: mqtt_client(), PubSub :: pubsub(), Topic :: binary()). -check_acl(Client, PubSub, Topic) when ?PUBSUB(PubSub) -> +check_acl(Client, PubSub, Topic) when ?PS(PubSub) -> case lookup_mods(acl) of [] -> case emqttd:env(allow_anonymous, false) of true -> allow; From 14d28d59bd957382b46f5e1d46fdcb8fb5b0c370 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Mon, 6 Mar 2017 18:47:50 +0800 Subject: [PATCH 101/125] Rename the 'Timestamp' variable to 'TS' --- src/emqttd_alarm.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/emqttd_alarm.erl b/src/emqttd_alarm.erl index f968db30e..1467797c7 100644 --- a/src/emqttd_alarm.erl +++ b/src/emqttd_alarm.erl @@ -87,14 +87,14 @@ handle_event({set_alarm, Alarm = #mqtt_alarm{id = AlarmId, severity = Severity, title = Title, summary = Summary}}, Alarms)-> - Timestamp = os:timestamp(), + TS = os:timestamp(), Json = mochijson2:encode([{id, AlarmId}, {severity, Severity}, {title, iolist_to_binary(Title)}, {summary, iolist_to_binary(Summary)}, - {ts, emqttd_time:now_secs(Timestamp)}]), + {ts, emqttd_time:now_secs(TS)}]), emqttd:publish(alarm_msg(alert, AlarmId, Json)), - {ok, [Alarm#mqtt_alarm{timestamp = Timestamp} | Alarms]}; + {ok, [Alarm#mqtt_alarm{timestamp = TS} | Alarms]}; handle_event({clear_alarm, AlarmId}, Alarms) -> Json = mochijson2:encode([{id, AlarmId}, {ts, emqttd_time:now_secs()}]), From 7c90e08f57f53a948d22ef4154755cd4822ae587 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Mon, 6 Mar 2017 18:48:29 +0800 Subject: [PATCH 102/125] Fix the subscrptions print --- src/emqttd_cli.erl | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/emqttd_cli.erl b/src/emqttd_cli.erl index cb4438cc8..54d462e65 100644 --- a/src/emqttd_cli.erl +++ b/src/emqttd_cli.erl @@ -537,20 +537,21 @@ print({Topic, Node}) -> ?PRINT("~s -> ~s~n", [Topic, Node]); print({ClientId, _ClientPid, _Persistent, SessInfo}) -> + Data = lists:append(SessInfo, emqttd_stats:get_session_stats(ClientId)), InfoKeys = [clean_sess, + subscriptions, max_inflight, - inflight_queue, - message_queue, - message_dropped, - awaiting_rel, - awaiting_ack, - awaiting_comp, + inflight_len, + mqueue_len, + mqueue_dropped, + awaiting_rel_len, + deliver_msg, + enqueue_msg, created_at], - ?PRINT("Session(~s, clean_sess=~s, max_inflight=~w, inflight_queue=~w, " - "message_queue=~w, message_dropped=~w, " - "awaiting_rel=~w, awaiting_ack=~w, awaiting_comp=~w, " - "created_at=~w)~n", - [ClientId | [format(Key, get_value(Key, SessInfo)) || Key <- InfoKeys]]). + ?PRINT("Session(~s, clean_sess=~s, max_inflight=~w, inflight=~w, " + "mqueue_len=~w, mqueue_dropped=~w, awaiting_rel=~w, " + "deliver_msg=~w, enqueue_msg=~w, created_at=~w)~n", + [ClientId | [format(Key, get_value(Key, Data)) || Key <- InfoKeys]]). print(subscription, {Sub, Topic}) when is_pid(Sub) -> ?PRINT("~p -> ~s~n", [Sub, Topic]); From e008d149d3900940dfb07807bf85b63fdfb14f63 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Mon, 6 Mar 2017 18:57:29 +0800 Subject: [PATCH 103/125] Update comments and misc fix --- src/emqttd_broker.erl | 2 +- src/emqttd_client.erl | 2 +- src/emqttd_serializer.erl | 2 +- src/emqttd_session.erl | 8 ++++---- src/emqttd_ws.erl | 4 ++-- src/emqttd_ws_client.erl | 5 +++-- src/emqttd_ws_client_sup.erl | 2 +- 7 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/emqttd_broker.erl b/src/emqttd_broker.erl index ce2e4210c..4f8ade17d 100644 --- a/src/emqttd_broker.erl +++ b/src/emqttd_broker.erl @@ -113,7 +113,7 @@ stop_tick(TRef) -> timer:cancel(TRef). %%-------------------------------------------------------------------- -%% gen_server callbacks +%% gen_server Callbacks %%-------------------------------------------------------------------- init([]) -> diff --git a/src/emqttd_client.erl b/src/emqttd_client.erl index e3e68b8e4..70a9729fa 100644 --- a/src/emqttd_client.erl +++ b/src/emqttd_client.erl @@ -291,7 +291,7 @@ code_change(_OldVsn, State, _Extra) -> %% Internal functions %%-------------------------------------------------------------------- -%% Receive and parse tcp data +%% Receive and Parse TCP Data received(<<>>, State) -> {noreply, gc(State), hibernate}; diff --git a/src/emqttd_serializer.erl b/src/emqttd_serializer.erl index a47a23c8b..079cfbb3c 100644 --- a/src/emqttd_serializer.erl +++ b/src/emqttd_serializer.erl @@ -28,7 +28,7 @@ %% @doc Serialise MQTT Packet -spec(serialize(mqtt_packet()) -> iolist()). -serialize(#mqtt_packet{header = Header = #mqtt_packet_header{type = Type}, +serialize(#mqtt_packet{header = Header = #mqtt_packet_header{type = Type}, variable = Variable, payload = Payload}) -> serialize_header(Header, diff --git a/src/emqttd_session.erl b/src/emqttd_session.erl index 288565a3a..85b027781 100644 --- a/src/emqttd_session.erl +++ b/src/emqttd_session.erl @@ -129,12 +129,12 @@ %% Client -> Broker: Inflight QoS2 messages received from client and waiting for pubrel. awaiting_rel :: map(), - %% Awaiting PUBREL timeout - await_rel_timeout = 20000 :: timeout(), - %% Max Packets that Awaiting PUBREL max_awaiting_rel = 100 :: non_neg_integer(), + %% Awaiting PUBREL timeout + await_rel_timeout = 20000 :: timeout(), + %% Awaiting PUBREL timer await_rel_timer :: reference(), @@ -580,7 +580,7 @@ code_change(_OldVsn, Session, _Extra) -> {ok, Session}. %%-------------------------------------------------------------------- -%% Kick old client +%% Kickout old client %%-------------------------------------------------------------------- kick(_ClientId, undefined, _Pid) -> ignore; diff --git a/src/emqttd_ws.erl b/src/emqttd_ws.erl index b292c39bc..c6a68150d 100644 --- a/src/emqttd_ws.erl +++ b/src/emqttd_ws.erl @@ -37,8 +37,8 @@ %% @doc Handle WebSocket Request. handle_request(Req) -> - {ok, Env} = emqttd:env(protocol), - PacketSize = get_value(max_packet_size, Env, ?MAX_PACKET_SIZE), + {ok, ProtoEnv} = emqttd:env(protocol), + PacketSize = get_value(max_packet_size, ProtoEnv, ?MAX_PACKET_SIZE), Parser = emqttd_parser:initial_state(PacketSize), %% Upgrade WebSocket. {ReentryWs, ReplyChannel} = mochiweb_websocket:upgrade_connection(Req, fun ?MODULE:ws_loop/3), diff --git a/src/emqttd_ws_client.erl b/src/emqttd_ws_client.erl index bde322dcf..c89fae7e4 100644 --- a/src/emqttd_ws_client.erl +++ b/src/emqttd_ws_client.erl @@ -92,14 +92,15 @@ init([Env, WsPid, Req, ReplyChannel]) -> {ok, Peername} = Req:get(peername), Headers = mochiweb_headers:to_list( mochiweb_request:get(headers, Req)), + Conn = Req:get(connection), ProtoState = emqttd_protocol:init(Peername, send_fun(ReplyChannel), [{ws_initial_headers, Headers} | Env]), IdleTimeout = get_value(client_idle_timeout, Env, 30000), EnableStats = get_value(client_enable_stats, Env, false), ForceGcCount = emqttd_gc:conn_max_gc_count(), - {ok, #wsclient_state{ws_pid = WsPid, + {ok, #wsclient_state{connection = Conn, + ws_pid = WsPid, peername = Peername, - connection = Req:get(connection), proto_state = ProtoState, enable_stats = EnableStats, force_gc_count = ForceGcCount}, diff --git a/src/emqttd_ws_client_sup.erl b/src/emqttd_ws_client_sup.erl index 1375ac036..21f683eaa 100644 --- a/src/emqttd_ws_client_sup.erl +++ b/src/emqttd_ws_client_sup.erl @@ -29,7 +29,7 @@ start_link() -> supervisor:start_link({local, ?MODULE}, ?MODULE, []). -%% @doc Start a WebSocket Client +%% @doc Start a WebSocket Connection. -spec(start_client(pid(), mochiweb_request:request(), fun()) -> {ok, pid()}). start_client(WsPid, Req, ReplyChannel) -> supervisor:start_child(?MODULE, [WsPid, Req, ReplyChannel]). From fba79b3e25cca674aac783fa57428b23bb5b7135 Mon Sep 17 00:00:00 2001 From: huangpj Date: Wed, 8 Mar 2017 18:01:59 +0800 Subject: [PATCH 104/125] support pbkdf2 --- Makefile | 3 ++- src/emqttd_auth_mod.erl | 10 +++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 913f11b89..558d66180 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ PROJECT_VERSION = 2.1.0 NO_AUTOPATCH = cuttlefish -DEPS = gproc lager esockd mochiweb lager_syslog +DEPS = gproc lager esockd mochiweb lager_syslog pbkdf2 dep_gproc = git https://github.com/uwiger/gproc dep_getopt = git https://github.com/jcomellas/getopt v0.8.2 @@ -12,6 +12,7 @@ dep_lager = git https://github.com/basho/lager master dep_esockd = git https://github.com/emqtt/esockd master dep_mochiweb = git https://github.com/emqtt/mochiweb dep_lager_syslog = git https://github.com/basho/lager_syslog +dep_pbkdf2 = git https://github.com/comtihon/erlang-pbkdf2.git 2.0.0 ERLC_OPTS += +'{parse_transform, lager_transform}' diff --git a/src/emqttd_auth_mod.erl b/src/emqttd_auth_mod.erl index c27f49d36..4ddf4c067 100644 --- a/src/emqttd_auth_mod.erl +++ b/src/emqttd_auth_mod.erl @@ -22,7 +22,7 @@ -export([passwd_hash/2]). --type(hash_type() :: plain | md5 | sha | sha256). +-type(hash_type() :: plain | md5 | sha | sha256 | pbkdf2). %%-------------------------------------------------------------------- %% Authentication behavihour @@ -51,7 +51,7 @@ behaviour_info(_Other) -> -endif. %% @doc Password Hash --spec(passwd_hash(hash_type(), binary()) -> binary()). +-spec(passwd_hash(hash_type(), binary() | tuple()) -> binary()). passwd_hash(plain, Password) -> Password; passwd_hash(md5, Password) -> @@ -59,7 +59,11 @@ passwd_hash(md5, Password) -> passwd_hash(sha, Password) -> hexstring(crypto:hash(sha, Password)); passwd_hash(sha256, Password) -> - hexstring(crypto:hash(sha256, Password)). + hexstring(crypto:hash(sha256, Password)); +passwd_hash(pbkdf2,{Salt,Password,Macfun,Iterations,Dklen}) -> + {ok,Hexstring} = pbkdf2:pbkdf2(Macfun, Password, Salt, Iterations, Dklen), + hexstring(Hexstring). + hexstring(<>) -> iolist_to_binary(io_lib:format("~32.16.0b", [X])); From 25c25826a7690c78d3cb6e6fc03b9dbaad84b957 Mon Sep 17 00:00:00 2001 From: huangpengju <3310324470@qq.com> Date: Thu, 9 Mar 2017 09:28:28 +0800 Subject: [PATCH 105/125] Update Makefile --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 558d66180..e137d5e3f 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ dep_lager = git https://github.com/basho/lager master dep_esockd = git https://github.com/emqtt/esockd master dep_mochiweb = git https://github.com/emqtt/mochiweb dep_lager_syslog = git https://github.com/basho/lager_syslog -dep_pbkdf2 = git https://github.com/comtihon/erlang-pbkdf2.git 2.0.0 +dep_pbkdf2 = git https://github.com/comtihon/erlang-pbkdf2.git 2.0.0 ERLC_OPTS += +'{parse_transform, lager_transform}' From 78b0f6691019139fc2599b3e4773d4a6aaf1aa22 Mon Sep 17 00:00:00 2001 From: huangpengju <3310324470@qq.com> Date: Thu, 9 Mar 2017 09:29:03 +0800 Subject: [PATCH 106/125] Update Makefile --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index e137d5e3f..6506d1fbe 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ dep_lager = git https://github.com/basho/lager master dep_esockd = git https://github.com/emqtt/esockd master dep_mochiweb = git https://github.com/emqtt/mochiweb dep_lager_syslog = git https://github.com/basho/lager_syslog -dep_pbkdf2 = git https://github.com/comtihon/erlang-pbkdf2.git 2.0.0 +dep_pbkdf2 = git https://github.com/comtihon/erlang-pbkdf2.git 2.0.0 ERLC_OPTS += +'{parse_transform, lager_transform}' From b5ff80499ad138d5bc545d5f352335be3e907813 Mon Sep 17 00:00:00 2001 From: huangpengju <3310324470@qq.com> Date: Thu, 9 Mar 2017 09:29:31 +0800 Subject: [PATCH 107/125] Update emqttd_auth_mod.erl --- src/emqttd_auth_mod.erl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/emqttd_auth_mod.erl b/src/emqttd_auth_mod.erl index 4ddf4c067..8f0b7405a 100644 --- a/src/emqttd_auth_mod.erl +++ b/src/emqttd_auth_mod.erl @@ -62,8 +62,7 @@ passwd_hash(sha256, Password) -> hexstring(crypto:hash(sha256, Password)); passwd_hash(pbkdf2,{Salt,Password,Macfun,Iterations,Dklen}) -> {ok,Hexstring} = pbkdf2:pbkdf2(Macfun, Password, Salt, Iterations, Dklen), - hexstring(Hexstring). - + pbkdf2:to_hex(Hexstring). hexstring(<>) -> iolist_to_binary(io_lib:format("~32.16.0b", [X])); From 61a71e7559f06f0cee6fbcc2db2f2a170e3b2497 Mon Sep 17 00:00:00 2001 From: huangpengju <3310324470@qq.com> Date: Sun, 12 Mar 2017 11:49:26 +0800 Subject: [PATCH 108/125] update emqttd_auth_mod.erl code format --- src/emqttd_auth_mod.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/emqttd_auth_mod.erl b/src/emqttd_auth_mod.erl index 8f0b7405a..ff7f79a20 100644 --- a/src/emqttd_auth_mod.erl +++ b/src/emqttd_auth_mod.erl @@ -60,7 +60,7 @@ passwd_hash(sha, Password) -> hexstring(crypto:hash(sha, Password)); passwd_hash(sha256, Password) -> hexstring(crypto:hash(sha256, Password)); -passwd_hash(pbkdf2,{Salt,Password,Macfun,Iterations,Dklen}) -> +passwd_hash(pbkdf2,{Salt, Password, Macfun, Iterations, Dklen}) -> {ok,Hexstring} = pbkdf2:pbkdf2(Macfun, Password, Salt, Iterations, Dklen), pbkdf2:to_hex(Hexstring). From 64724573427faaf4a2c7e406513f2100d0975d43 Mon Sep 17 00:00:00 2001 From: Frank Feng Date: Sun, 12 Mar 2017 23:06:57 +0800 Subject: [PATCH 109/125] Match {error,einval} --- src/emqttd_client.erl | 9 +++++++-- src/emqttd_keepalive.erl | 25 ++++++++++++++----------- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/emqttd_client.erl b/src/emqttd_client.erl index e3e68b8e4..a6ddd9eb2 100644 --- a/src/emqttd_client.erl +++ b/src/emqttd_client.erl @@ -252,8 +252,13 @@ handle_info({keepalive, start, Interval}, State = #client_state{connection = Con {error, Error} -> {error, Error} end end, - KeepAlive = emqttd_keepalive:start(StatFun, Interval, {keepalive, check}), - {noreply, State#client_state{keepalive = KeepAlive}, hibernate}; + case emqttd_keepalive:start(StatFun, Interval, {keepalive, check}) of + {ok, KeepAlive} -> + {noreply, State#client_state{keepalive = KeepAlive}, hibernate}; + {error, Error} -> + ?LOG(warning, "Keepalive error - ~p", [Error], State), + shutdown(Error, State) + end; handle_info({keepalive, check}, State = #client_state{keepalive = KeepAlive}) -> case emqttd_keepalive:check(KeepAlive) of diff --git a/src/emqttd_keepalive.erl b/src/emqttd_keepalive.erl index 51d45c89c..ae8f6ffbe 100644 --- a/src/emqttd_keepalive.erl +++ b/src/emqttd_keepalive.erl @@ -29,14 +29,18 @@ -export_type([keepalive/0]). %% @doc Start a keepalive --spec(start(fun(), integer(), any()) -> undefined | keepalive()). +-spec(start(fun(), integer(), any()) -> {ok, keepalive()} | {error, any()}). start(_, 0, _) -> - undefined; + {ok, #keepalive{}}; start(StatFun, TimeoutSec, TimeoutMsg) -> - {ok, StatVal} = StatFun(), - #keepalive{statfun = StatFun, statval = StatVal, - tsec = TimeoutSec, tmsg = TimeoutMsg, - tref = timer(TimeoutSec, TimeoutMsg)}. + case StatFun() of + {ok, StatVal} -> + {ok, #keepalive{statfun = StatFun, statval = StatVal, + tsec = TimeoutSec, tmsg = TimeoutMsg, + tref = timer(TimeoutSec, TimeoutMsg)}}; + {error, Error} -> + {error, Error} + end. %% @doc Check keepalive, called when timeout. -spec(check(keepalive()) -> {ok, keepalive()} | {error, any()}). @@ -59,12 +63,11 @@ resume(KeepAlive = #keepalive{tsec = TimeoutSec, tmsg = TimeoutMsg}) -> %% @doc Cancel Keepalive -spec(cancel(keepalive()) -> ok). -cancel(#keepalive{tref = TRef}) -> - cancel(TRef); -cancel(undefined) -> +cancel(#keepalive{tref = TRef}) when is_reference(TRef) -> + erlang:cancel_timer(TRef), ok; -cancel(TRef) -> - catch erlang:cancel_timer(TRef). +cancel(_) -> + ok. timer(Sec, Msg) -> erlang:send_after(timer:seconds(Sec), self(), Msg). From 7cb3b7ca8a5121bf28b575268e5b4e36f5e8c714 Mon Sep 17 00:00:00 2001 From: Frank Feng Date: Sun, 12 Mar 2017 23:07:45 +0800 Subject: [PATCH 110/125] Id -> I --- include/emqttd_internal.hrl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/emqttd_internal.hrl b/include/emqttd_internal.hrl index fc482313f..72d745646 100644 --- a/include/emqttd_internal.hrl +++ b/include/emqttd_internal.hrl @@ -19,7 +19,7 @@ -define(GPROC_POOL(JoinOrLeave, Pool, I), (begin case JoinOrLeave of - join -> gproc_pool:connect_worker(Pool, {Pool, Id}); + join -> gproc_pool:connect_worker(Pool, {Pool, I}); leave -> gproc_pool:disconnect_worker(Pool, {Pool, I}) end end)). From 0df59934c7704300964a05917d95e984ae79ea7a Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Mon, 13 Mar 2017 12:14:57 +0800 Subject: [PATCH 111/125] Fix the GPROC_POOL macro --- include/emqttd_internal.hrl | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/include/emqttd_internal.hrl b/include/emqttd_internal.hrl index 72d745646..ede858916 100644 --- a/include/emqttd_internal.hrl +++ b/include/emqttd_internal.hrl @@ -16,19 +16,18 @@ %% Internal Header File --define(GPROC_POOL(JoinOrLeave, Pool, I), +-define(GPROC_POOL(JoinOrLeave, Pool, Id), (begin case JoinOrLeave of - join -> gproc_pool:connect_worker(Pool, {Pool, I}); - leave -> gproc_pool:disconnect_worker(Pool, {Pool, I}) + join -> gproc_pool:connect_worker(Pool, {Pool, Id}); + leave -> gproc_pool:disconnect_worker(Pool, {Pool, Id}) end end)). -define(PROC_NAME(M, I), (list_to_atom(lists:concat([M, "_", I])))). -define(record_to_proplist(Def, Rec), - lists:zip(record_info(fields, Def), - tl(tuple_to_list(Rec)))). + lists:zip(record_info(fields, Def), tl(tuple_to_list(Rec)))). -define(record_to_proplist(Def, Rec, Fields), [{K, V} || {K, V} <- ?record_to_proplist(Def, Rec), From 90e46325df0f05a39ff47c7161b209540bef591e Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Mon, 13 Mar 2017 12:42:02 +0800 Subject: [PATCH 112/125] Fix the crash caused by keepalive:start/3 --- src/emqttd_client.erl | 3 ++- src/emqttd_keepalive.erl | 7 +++---- src/emqttd_ws_client.erl | 9 +++++++-- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/emqttd_client.erl b/src/emqttd_client.erl index 998c9f7af..d9543180b 100644 --- a/src/emqttd_client.erl +++ b/src/emqttd_client.erl @@ -252,7 +252,7 @@ handle_info({keepalive, start, Interval}, State = #client_state{connection = Con {error, Error} -> {error, Error} end end, - case emqttd_keepalive:start(StatFun, Interval, {keepalive, check}) of + case emqttd_keepalive:start(Statfun, Interval, {keepalive, check}) of {ok, KeepAlive} -> {noreply, State#client_state{keepalive = KeepAlive}, hibernate}; {error, Error} -> @@ -379,3 +379,4 @@ stop(Reason, State) -> gc(State) -> emqttd_gc:maybe_force_gc(#client_state.force_gc_count, State). + diff --git a/src/emqttd_keepalive.erl b/src/emqttd_keepalive.erl index ae8f6ffbe..5d26ea8a6 100644 --- a/src/emqttd_keepalive.erl +++ b/src/emqttd_keepalive.erl @@ -36,8 +36,8 @@ start(StatFun, TimeoutSec, TimeoutMsg) -> case StatFun() of {ok, StatVal} -> {ok, #keepalive{statfun = StatFun, statval = StatVal, - tsec = TimeoutSec, tmsg = TimeoutMsg, - tref = timer(TimeoutSec, TimeoutMsg)}}; + tsec = TimeoutSec, tmsg = TimeoutMsg, + tref = timer(TimeoutSec, TimeoutMsg)}}; {error, Error} -> {error, Error} end. @@ -64,8 +64,7 @@ resume(KeepAlive = #keepalive{tsec = TimeoutSec, tmsg = TimeoutMsg}) -> %% @doc Cancel Keepalive -spec(cancel(keepalive()) -> ok). cancel(#keepalive{tref = TRef}) when is_reference(TRef) -> - erlang:cancel_timer(TRef), - ok; + catch erlang:cancel_timer(TRef), ok; cancel(_) -> ok. diff --git a/src/emqttd_ws_client.erl b/src/emqttd_ws_client.erl index c89fae7e4..cbe6ab51c 100644 --- a/src/emqttd_ws_client.erl +++ b/src/emqttd_ws_client.erl @@ -198,8 +198,13 @@ handle_info({shutdown, conflict, {ClientId, NewPid}}, State) -> handle_info({keepalive, start, Interval}, State = #wsclient_state{connection = Conn}) -> ?WSLOG(debug, "Keepalive at the interval of ~p", [Interval], State), - KeepAlive = emqttd_keepalive:start(stat_fun(Conn), Interval, {keepalive, check}), - {noreply, State#wsclient_state{keepalive = KeepAlive}, hibernate}; + case emqttd_keepalive:start(stat_fun(Conn), Interval, {keepalive, check}) of + {ok, KeepAlive} -> + {noreply, State#wsclient_state{keepalive = KeepAlive}, hibernate}; + {error, Error} -> + ?LOG(warning, "Keepalive error - ~p", [Error], State), + shutdown(Error, State) + end; handle_info({keepalive, check}, State = #wsclient_state{keepalive = KeepAlive}) -> case emqttd_keepalive:check(KeepAlive) of From 05396f0adef43fb5a879fb1ac1f6bbe70d346eef Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Mon, 13 Mar 2017 13:30:49 +0800 Subject: [PATCH 113/125] Add pbkdf2 dependencies and fix the building errors --- rebar.config | 2 +- src/emqttd_client.erl | 2 +- src/emqttd_ws_client.erl | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/rebar.config b/rebar.config index 0b43df393..8fb62e95f 100644 --- a/rebar.config +++ b/rebar.config @@ -1,4 +1,4 @@ {deps, [ -{gproc,".*",{git,"https://github.com/uwiger/gproc",""}},{lager,".*",{git,"https://github.com/basho/lager","master"}},{esockd,".*",{git,"https://github.com/emqtt/esockd","master"}},{mochiweb,".*",{git,"https://github.com/emqtt/mochiweb",""}},{lager_syslog,".*",{git,"https://github.com/basho/lager_syslog",""}} +{gproc,".*",{git,"https://github.com/uwiger/gproc",""}},{lager,".*",{git,"https://github.com/basho/lager","master"}},{esockd,".*",{git,"https://github.com/emqtt/esockd","master"}},{mochiweb,".*",{git,"https://github.com/emqtt/mochiweb",""}},{lager_syslog,".*",{git,"https://github.com/basho/lager_syslog",""}},{pbkdf2,".*",{git,"https://github.com/comtihon/erlang-pbkdf2.git","2.0.0"}} ]}. {erl_opts, [{parse_transform,lager_transform}]}. diff --git a/src/emqttd_client.erl b/src/emqttd_client.erl index d9543180b..f8794cd92 100644 --- a/src/emqttd_client.erl +++ b/src/emqttd_client.erl @@ -252,7 +252,7 @@ handle_info({keepalive, start, Interval}, State = #client_state{connection = Con {error, Error} -> {error, Error} end end, - case emqttd_keepalive:start(Statfun, Interval, {keepalive, check}) of + case emqttd_keepalive:start(StatFun, Interval, {keepalive, check}) of {ok, KeepAlive} -> {noreply, State#client_state{keepalive = KeepAlive}, hibernate}; {error, Error} -> diff --git a/src/emqttd_ws_client.erl b/src/emqttd_ws_client.erl index cbe6ab51c..4fb57b6ad 100644 --- a/src/emqttd_ws_client.erl +++ b/src/emqttd_ws_client.erl @@ -202,7 +202,7 @@ handle_info({keepalive, start, Interval}, State = #wsclient_state{connection = C {ok, KeepAlive} -> {noreply, State#wsclient_state{keepalive = KeepAlive}, hibernate}; {error, Error} -> - ?LOG(warning, "Keepalive error - ~p", [Error], State), + ?WSLOG(warning, "Keepalive error - ~p", [Error], State), shutdown(Error, State) end; From 056dc747ada16875c69e4867e53642c7a5c6a152 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Mon, 13 Mar 2017 14:56:59 +0800 Subject: [PATCH 114/125] Depend on emq20 branch of esockd library --- Makefile | 2 +- rebar.config | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 6506d1fbe..b35c84f88 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,7 @@ DEPS = gproc lager esockd mochiweb lager_syslog pbkdf2 dep_gproc = git https://github.com/uwiger/gproc dep_getopt = git https://github.com/jcomellas/getopt v0.8.2 dep_lager = git https://github.com/basho/lager master -dep_esockd = git https://github.com/emqtt/esockd master +dep_esockd = git https://github.com/emqtt/esockd emq20 dep_mochiweb = git https://github.com/emqtt/mochiweb dep_lager_syslog = git https://github.com/basho/lager_syslog dep_pbkdf2 = git https://github.com/comtihon/erlang-pbkdf2.git 2.0.0 diff --git a/rebar.config b/rebar.config index 8fb62e95f..659b453cf 100644 --- a/rebar.config +++ b/rebar.config @@ -1,4 +1,4 @@ {deps, [ -{gproc,".*",{git,"https://github.com/uwiger/gproc",""}},{lager,".*",{git,"https://github.com/basho/lager","master"}},{esockd,".*",{git,"https://github.com/emqtt/esockd","master"}},{mochiweb,".*",{git,"https://github.com/emqtt/mochiweb",""}},{lager_syslog,".*",{git,"https://github.com/basho/lager_syslog",""}},{pbkdf2,".*",{git,"https://github.com/comtihon/erlang-pbkdf2.git","2.0.0"}} +{gproc,".*",{git,"https://github.com/uwiger/gproc",""}},{lager,".*",{git,"https://github.com/basho/lager","master"}},{esockd,".*",{git,"https://github.com/emqtt/esockd","emq20"}},{mochiweb,".*",{git,"https://github.com/emqtt/mochiweb",""}},{lager_syslog,".*",{git,"https://github.com/basho/lager_syslog",""}},{pbkdf2,".*",{git,"https://github.com/comtihon/erlang-pbkdf2.git","2.0.0"}} ]}. {erl_opts, [{parse_transform,lager_transform}]}. From 6557dbe967347026be8b8ce71e352b510a5ca7ff Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Mon, 13 Mar 2017 15:08:29 +0800 Subject: [PATCH 115/125] Change 'ssl' option to 'sslopts' --- priv/emq.schema | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/priv/emq.schema b/priv/emq.schema index b5f40b893..697c6c7ae 100644 --- a/priv/emq.schema +++ b/priv/emq.schema @@ -725,7 +725,7 @@ end}. ConnOpts = Filter([{rate_limit, cuttlefish:conf_get(Key ++ ".rate_limit", Conf, undefined)}]), Opts = [{connopts, ConnOpts}, {sockopts, TcpOpts(Key)} | LisOpts(Key)], [{Name, Port, case Name =:= ssl orelse Name =:= https of - true -> [{ssl, SslOpts(Key)} | Opts]; + true -> [{sslopts, SslOpts(Key)} | Opts]; false -> Opts end}] end From d099d0b53cc5288f29ea9d8bd64f05899c73da42 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Mon, 13 Mar 2017 15:36:58 +0800 Subject: [PATCH 116/125] Add emqttd_gc:maybe_force_gc/3 API and tune the min hibernate interval --- src/emqttd_client.erl | 7 ++++--- src/emqttd_gc.erl | 7 +++++-- src/emqttd_ws_client.erl | 2 +- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/emqttd_client.erl b/src/emqttd_client.erl index f8794cd92..88f14c2d6 100644 --- a/src/emqttd_client.erl +++ b/src/emqttd_client.erl @@ -127,7 +127,7 @@ do_init(Conn, Env, Peername) -> force_gc_count = ForceGcCount}), IdleTimout = get_value(client_idle_timeout, Env, 30000), gen_server2:enter_loop(?MODULE, [], State, self(), IdleTimout, - {backoff, 1000, 1000, 10000}). + {backoff, 2000, 2000, 20000}). send_fun(Conn, Peername) -> Self = self(), @@ -377,6 +377,7 @@ shutdown(Reason, State) -> stop(Reason, State) -> {stop, Reason, State}. -gc(State) -> - emqttd_gc:maybe_force_gc(#client_state.force_gc_count, State). +gc(State = #client_state{connection = Conn}) -> + Cb = fun() -> Conn:gc() end, + emqttd_gc:maybe_force_gc(#client_state.force_gc_count, State, Cb). diff --git a/src/emqttd_gc.erl b/src/emqttd_gc.erl index 04cdbf2d5..75545a77f 100644 --- a/src/emqttd_gc.erl +++ b/src/emqttd_gc.erl @@ -20,7 +20,8 @@ -author("Feng Lee "). --export([conn_max_gc_count/0, reset_conn_gc_count/2, maybe_force_gc/2]). +-export([conn_max_gc_count/0, reset_conn_gc_count/2, maybe_force_gc/2, + maybe_force_gc/3]). -spec(conn_max_gc_count() -> integer()). conn_max_gc_count() -> @@ -38,9 +39,11 @@ reset_conn_gc_count(Pos, State) -> end. maybe_force_gc(Pos, State) -> + maybe_force_gc(Pos, State, fun() -> ok end). +maybe_force_gc(Pos, State, Cb) -> case element(Pos, State) of undefined -> State; - I when I =< 0 -> garbage_collect(), + I when I =< 0 -> Cb(), garbage_collect(), reset_conn_gc_count(Pos, State); I -> setelement(Pos, State, I - 1) end. diff --git a/src/emqttd_ws_client.erl b/src/emqttd_ws_client.erl index 4fb57b6ad..4beb7bc40 100644 --- a/src/emqttd_ws_client.erl +++ b/src/emqttd_ws_client.erl @@ -104,7 +104,7 @@ init([Env, WsPid, Req, ReplyChannel]) -> proto_state = ProtoState, enable_stats = EnableStats, force_gc_count = ForceGcCount}, - IdleTimeout, {backoff, 1000, 1000, 10000}, ?MODULE}. + IdleTimeout, {backoff, 2000, 2000, 20000}, ?MODULE}. prioritise_call(Msg, _From, _Len, _State) -> case Msg of info -> 10; stats -> 10; state -> 10; _ -> 5 end. From 82bb7766ac0e3400487e528c9677a4197588b408 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Mon, 13 Mar 2017 15:55:00 +0800 Subject: [PATCH 117/125] Add env, maintainers, licenses properties --- src/emqttd.app.src | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/emqttd.app.src b/src/emqttd.app.src index c86af2a0a..3f0c2984c 100644 --- a/src/emqttd.app.src +++ b/src/emqttd.app.src @@ -1,8 +1,12 @@ {application, emqttd, [ - {description, "Erlang MQTT Broker"}, - {vsn, "2.1.0"}, - {modules, []}, - {registered, [emqttd_sup]}, - {applications, [kernel,stdlib,gproc,lager,esockd,mochiweb,lager_syslog]}, - {mod, {emqttd_app, []}} + {description, "Erlang MQTT Broker"}, + {vsn, "2.1.0"}, + {modules, []}, + {registered, [emqttd_sup]}, + {applications, [kernel,stdlib,gproc,lager,esockd,mochiweb,lager_syslog,pbkdf2]}, + {env, []}, + {mod, {emqttd_app, []}} + {maintainers, ["Feng Lee "]}, + {licenses, ["MIT"]}, + {links, [{"Github", "https://github.com/emqtt/emqttd"}]} ]}. From 1de9496b4ea5a26771ec261698c144aed9920c9a Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Mon, 13 Mar 2017 16:01:48 +0800 Subject: [PATCH 118/125] Upgrade Erlang/OTP to R19+ --- .travis.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index a4cef5371..15834e620 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,9 @@ language: erlang otp_release: - - 18.0 - - 18.1 - - 18.2.1 - - 18.3 + - 19.0 + - 19.1 + - 19.2 script: - make From 524dce40dd8ce3c360a43252de68db1187510beb Mon Sep 17 00:00:00 2001 From: HuangDan Date: Mon, 13 Mar 2017 16:08:22 +0800 Subject: [PATCH 119/125] Fix test case --- test/emqttd_net_SUITE.erl | 2 +- test/emqttd_protocol_SUITE.erl | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/test/emqttd_net_SUITE.erl b/test/emqttd_net_SUITE.erl index e3ae0700c..78abb50c9 100644 --- a/test/emqttd_net_SUITE.erl +++ b/test/emqttd_net_SUITE.erl @@ -28,7 +28,7 @@ groups() -> [{keepalive, [], [t_keepalive]}]. %%-------------------------------------------------------------------- t_keepalive(_) -> - KA = emqttd_keepalive:start(fun() -> {ok, 1} end, 1, {keepalive, timeout}), + {ok, KA} = emqttd_keepalive:start(fun() -> {ok, 1} end, 1, {keepalive, timeout}), [resumed, timeout] = lists:reverse(keepalive_recv(KA, [])). keepalive_recv(KA, Acc) -> diff --git a/test/emqttd_protocol_SUITE.erl b/test/emqttd_protocol_SUITE.erl index 1ac688650..21428f0c7 100644 --- a/test/emqttd_protocol_SUITE.erl +++ b/test/emqttd_protocol_SUITE.erl @@ -22,6 +22,8 @@ -include("emqttd.hrl"). +-include_lib("eunit/include/eunit.hrl"). + -include("emqttd_protocol.hrl"). all() -> @@ -344,7 +346,7 @@ message_make(_) -> message_from_packet(_) -> Msg = emqttd_message:from_packet(?PUBLISH_PACKET(1, <<"topic">>, 10, <<"payload">>)), ?assertEqual(1, Msg#mqtt_message.qos), - ?assertEqual(10, Msg#mqtt_message.packet_id), + ?assertEqual(10, Msg#mqtt_message.pktid), ?assertEqual(<<"topic">>, Msg#mqtt_message.topic), WillMsg = emqttd_message:from_packet(#mqtt_packet_connect{will_flag = true, will_topic = <<"WillTopic">>, From 2e6e97b0064afc7e7b7e71fea759d81b1e127e19 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Mon, 13 Mar 2017 16:30:41 +0800 Subject: [PATCH 120/125] Fix the syntax error --- src/emqttd.app.src | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/emqttd.app.src b/src/emqttd.app.src index 3f0c2984c..d03a4d952 100644 --- a/src/emqttd.app.src +++ b/src/emqttd.app.src @@ -5,7 +5,7 @@ {registered, [emqttd_sup]}, {applications, [kernel,stdlib,gproc,lager,esockd,mochiweb,lager_syslog,pbkdf2]}, {env, []}, - {mod, {emqttd_app, []}} + {mod, {emqttd_app, []}}, {maintainers, ["Feng Lee "]}, {licenses, ["MIT"]}, {links, [{"Github", "https://github.com/emqtt/emqttd"}]} From b5446ceb4de715962bf50fa4f714f4f54265a815 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Mon, 13 Mar 2017 16:35:36 +0800 Subject: [PATCH 121/125] Change the 'ssl' to 'sslopts' --- test/emqttd_SUITE_data/emqttd.schema | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/emqttd_SUITE_data/emqttd.schema b/test/emqttd_SUITE_data/emqttd.schema index 156bc4874..530984383 100644 --- a/test/emqttd_SUITE_data/emqttd.schema +++ b/test/emqttd_SUITE_data/emqttd.schema @@ -710,7 +710,7 @@ end}. ConnOpts = Filter([{rate_limit, cuttlefish:conf_get(Key ++ ".rate_limit", Conf, undefined)}]), Opts = [{connopts, ConnOpts}, {sockopts, TcpOpts(Key)} | LisOpts(Key)], [{Name, Port, case Name =:= ssl orelse Name =:= https of - true -> [{ssl, SslOpts(Key)} | Opts]; + true -> [{sslopts, SslOpts(Key)} | Opts]; false -> Opts end}] end From ecbc1cf56b489665a125ec68e6e30180cdeccaaa Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Mon, 13 Mar 2017 16:51:46 +0800 Subject: [PATCH 122/125] Format the 'dep_' lines --- Makefile | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index b35c84f88..cf1234a02 100644 --- a/Makefile +++ b/Makefile @@ -6,13 +6,13 @@ NO_AUTOPATCH = cuttlefish DEPS = gproc lager esockd mochiweb lager_syslog pbkdf2 -dep_gproc = git https://github.com/uwiger/gproc -dep_getopt = git https://github.com/jcomellas/getopt v0.8.2 -dep_lager = git https://github.com/basho/lager master -dep_esockd = git https://github.com/emqtt/esockd emq20 -dep_mochiweb = git https://github.com/emqtt/mochiweb -dep_lager_syslog = git https://github.com/basho/lager_syslog -dep_pbkdf2 = git https://github.com/comtihon/erlang-pbkdf2.git 2.0.0 +dep_gproc = git https://github.com/uwiger/gproc +dep_getopt = git https://github.com/jcomellas/getopt v0.8.2 +dep_lager = git https://github.com/basho/lager master +dep_esockd = git https://github.com/emqtt/esockd emq20 +dep_mochiweb = git https://github.com/emqtt/mochiweb +dep_lager_syslog = git https://github.com/basho/lager_syslog +dep_pbkdf2 = git https://github.com/comtihon/erlang-pbkdf2.git 2.0.0 ERLC_OPTS += +'{parse_transform, lager_transform}' From 3ebcb71e9f30b24c2d70d03b004a9ee5b156a538 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Mon, 13 Mar 2017 17:03:34 +0800 Subject: [PATCH 123/125] Upgrade esockd library to v4.2 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index cf1234a02..80000fcf1 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,7 @@ DEPS = gproc lager esockd mochiweb lager_syslog pbkdf2 dep_gproc = git https://github.com/uwiger/gproc dep_getopt = git https://github.com/jcomellas/getopt v0.8.2 dep_lager = git https://github.com/basho/lager master -dep_esockd = git https://github.com/emqtt/esockd emq20 +dep_esockd = git https://github.com/emqtt/esockd v4.2 dep_mochiweb = git https://github.com/emqtt/mochiweb dep_lager_syslog = git https://github.com/basho/lager_syslog dep_pbkdf2 = git https://github.com/comtihon/erlang-pbkdf2.git 2.0.0 From 076b3c00688f58b62758bdc6328dabcff32bfd06 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Mon, 13 Mar 2017 17:10:54 +0800 Subject: [PATCH 124/125] Upgrade esockd library to v4.2 --- rebar.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rebar.config b/rebar.config index 659b453cf..4cc2a59d1 100644 --- a/rebar.config +++ b/rebar.config @@ -1,4 +1,4 @@ {deps, [ -{gproc,".*",{git,"https://github.com/uwiger/gproc",""}},{lager,".*",{git,"https://github.com/basho/lager","master"}},{esockd,".*",{git,"https://github.com/emqtt/esockd","emq20"}},{mochiweb,".*",{git,"https://github.com/emqtt/mochiweb",""}},{lager_syslog,".*",{git,"https://github.com/basho/lager_syslog",""}},{pbkdf2,".*",{git,"https://github.com/comtihon/erlang-pbkdf2.git","2.0.0"}} +{gproc,".*",{git,"https://github.com/uwiger/gproc",""}},{lager,".*",{git,"https://github.com/basho/lager","master"}},{esockd,".*",{git,"https://github.com/emqtt/esockd","v4.2"}},{mochiweb,".*",{git,"https://github.com/emqtt/mochiweb",""}},{lager_syslog,".*",{git,"https://github.com/basho/lager_syslog",""}},{pbkdf2,".*",{git,"https://github.com/comtihon/erlang-pbkdf2.git","2.0.0"}} ]}. {erl_opts, [{parse_transform,lager_transform}]}. From b3cb875eb2e6cf7d4a11a852a5695492afaaf05f Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Wed, 22 Mar 2017 10:13:17 +0800 Subject: [PATCH 125/125] Depends on the 'emq22' branch of esockd, mochiweb --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 80000fcf1..5b46ef5ce 100644 --- a/Makefile +++ b/Makefile @@ -9,8 +9,8 @@ DEPS = gproc lager esockd mochiweb lager_syslog pbkdf2 dep_gproc = git https://github.com/uwiger/gproc dep_getopt = git https://github.com/jcomellas/getopt v0.8.2 dep_lager = git https://github.com/basho/lager master -dep_esockd = git https://github.com/emqtt/esockd v4.2 -dep_mochiweb = git https://github.com/emqtt/mochiweb +dep_esockd = git https://github.com/emqtt/esockd emq22 +dep_mochiweb = git https://github.com/emqtt/mochiweb emq22 dep_lager_syslog = git https://github.com/basho/lager_syslog dep_pbkdf2 = git https://github.com/comtihon/erlang-pbkdf2.git 2.0.0