From eb4b9936dcc19fad4266fd5a62bf798250d3688e Mon Sep 17 00:00:00 2001 From: turtled Date: Sun, 11 Oct 2020 11:12:39 +0800 Subject: [PATCH 01/26] fix(modules): fix conflicts --- etc/acl.conf | 26 - etc/acl.conf.paho | 14 - etc/cluster.conf | 170 ++ etc/emqx.conf | 1938 +-------------------- etc/listeners.conf | 928 ++++++++++ etc/logger.conf | 170 ++ etc/rpc.conf | 98 ++ etc/sys_mon.conf | 148 ++ etc/zones.conf | 327 ++++ priv/emqx.schema | 112 +- rebar.config | 2 +- src/emqx_access_rule.erl | 155 -- src/emqx_alarm.erl | 8 +- src/emqx_app.erl | 10 +- src/emqx_channel.erl | 59 +- src/emqx_cm_locker.erl | 2 +- src/emqx_gen_mod.erl | 23 - src/emqx_mod_acl_internal.erl | 122 -- src/emqx_mod_delayed.erl | 204 --- src/emqx_mod_presence.erl | 130 -- src/emqx_mod_rewrite.erl | 103 -- src/emqx_mod_subscription.erl | 65 - src/emqx_mod_sup.erl | 63 - src/emqx_mod_topic_metrics.erl | 382 ---- src/emqx_modules.erl | 169 -- src/emqx_session.erl | 2 +- src/emqx_sup.erl | 3 +- src/emqx_types.erl | 1 + test/emqx_access_SUITE_data/acl_temp.conf | 1 + test/emqx_access_rule_SUITE.erl | 97 -- test/emqx_acl_cache_SUITE.erl | 23 +- test/emqx_acl_test_mod.erl | 33 - test/emqx_channel_SUITE.erl | 15 +- test/emqx_mod_acl_internal_SUITE.erl | 64 - test/emqx_mod_delayed_SUITE.erl | 78 - test/emqx_mod_presence_SUITE.erl | 88 - test/emqx_mod_rewrite_SUITE.erl | 93 - test/emqx_mod_subscription_SUITE.erl | 92 - test/emqx_mod_sup_SUITE.erl | 51 - test/emqx_mod_topic_metrics_SUITE.erl | 92 - test/emqx_modules_SUITE.erl | 47 - test/mqtt_protocol_v5_SUITE.erl | 8 +- 42 files changed, 1933 insertions(+), 4283 deletions(-) delete mode 100644 etc/acl.conf delete mode 100644 etc/acl.conf.paho create mode 100644 etc/cluster.conf create mode 100644 etc/listeners.conf create mode 100644 etc/logger.conf create mode 100644 etc/rpc.conf create mode 100644 etc/sys_mon.conf create mode 100644 etc/zones.conf delete mode 100644 src/emqx_access_rule.erl delete mode 100644 src/emqx_gen_mod.erl delete mode 100644 src/emqx_mod_acl_internal.erl delete mode 100644 src/emqx_mod_delayed.erl delete mode 100644 src/emqx_mod_presence.erl delete mode 100644 src/emqx_mod_rewrite.erl delete mode 100644 src/emqx_mod_subscription.erl delete mode 100644 src/emqx_mod_sup.erl delete mode 100644 src/emqx_mod_topic_metrics.erl delete mode 100644 src/emqx_modules.erl create mode 100644 test/emqx_access_SUITE_data/acl_temp.conf delete mode 100644 test/emqx_access_rule_SUITE.erl delete mode 100644 test/emqx_acl_test_mod.erl delete mode 100644 test/emqx_mod_acl_internal_SUITE.erl delete mode 100644 test/emqx_mod_delayed_SUITE.erl delete mode 100644 test/emqx_mod_presence_SUITE.erl delete mode 100644 test/emqx_mod_rewrite_SUITE.erl delete mode 100644 test/emqx_mod_subscription_SUITE.erl delete mode 100644 test/emqx_mod_sup_SUITE.erl delete mode 100644 test/emqx_mod_topic_metrics_SUITE.erl delete mode 100644 test/emqx_modules_SUITE.erl diff --git a/etc/acl.conf b/etc/acl.conf deleted file mode 100644 index af2fb0dd1..000000000 --- a/etc/acl.conf +++ /dev/null @@ -1,26 +0,0 @@ -%%-------------------------------------------------------------------- -%% [ACL](https://docs.emqx.io/broker/v3/en/config.html) -%% -%% -type(who() :: all | binary() | -%% {ipaddr, esockd_access:cidr()} | -%% {client, binary()} | -%% {user, binary()}). -%% -%% -type(access() :: subscribe | publish | pubsub). -%% -%% -type(topic() :: binary()). -%% -%% -type(rule() :: {allow, all} | -%% {allow, who(), access(), list(topic())} | -%% {deny, all} | -%% {deny, who(), access(), list(topic())}). -%%-------------------------------------------------------------------- - -{allow, {user, "dashboard"}, subscribe, ["$SYS/#"]}. - -{allow, {ipaddr, "127.0.0.1"}, pubsub, ["$SYS/#", "#"]}. - -{deny, all, subscribe, ["$SYS/#", {eq, "#"}]}. - -{allow, all}. - diff --git a/etc/acl.conf.paho b/etc/acl.conf.paho deleted file mode 100644 index 5beec4347..000000000 --- a/etc/acl.conf.paho +++ /dev/null @@ -1,14 +0,0 @@ -%%-------------------------------------------------------------------- -%% For paho interoperability test cases -%%-------------------------------------------------------------------- - -{deny, {client, "myclientid"}, subscribe, ["test/nosubscribe"]}. - -{allow, {user, "dashboard"}, subscribe, ["$SYS/#"]}. - -{allow, {ipaddr, "127.0.0.1"}, pubsub, ["$SYS/#", "#"]}. - -{deny, all, subscribe, ["$SYS/#", {eq, "#"}]}. - -{allow, all}. - diff --git a/etc/cluster.conf b/etc/cluster.conf new file mode 100644 index 000000000..cd4d3d007 --- /dev/null +++ b/etc/cluster.conf @@ -0,0 +1,170 @@ +##-------------------------------------------------------------------- +## Cluster +##-------------------------------------------------------------------- + +## Cluster name. +## +## Value: String +cluster.name = emqxcl + +## Specify the erlang distributed protocol. +## +## Value: Enum +## - inet_tcp: the default; handles TCP streams with IPv4 addressing. +## - inet6_tcp: handles TCP with IPv6 addressing. +## - inet_tls: using TLS for Erlang Distribution. +## +## vm.args: -proto_dist inet_tcp +cluster.proto_dist = inet_tcp + +## Cluster auto-discovery strategy. +## +## Value: Enum +## - manual: Manual join command +## - static: Static node list +## - mcast: IP Multicast +## - dns: DNS A Record +## - etcd: etcd +## - k8s: Kubernetes +## +## Default: manual +cluster.discovery = manual + +## Enable cluster autoheal from network partition. +## +## Value: on | off +## +## Default: on +cluster.autoheal = on + +## Autoclean down node. A down node will be removed from the cluster +## if this value > 0. +## +## Value: Duration +## -h: hour, e.g. '2h' for 2 hours +## -m: minute, e.g. '5m' for 5 minutes +## -s: second, e.g. '30s' for 30 seconds +## +## Default: 5m +cluster.autoclean = 5m + +##-------------------------------------------------------------------- +## Cluster using static node list + +## Node list of the cluster. +## +## Value: String +## cluster.static.seeds = emqx1@127.0.0.1,emqx2@127.0.0.1 + +##-------------------------------------------------------------------- +## Cluster using IP Multicast. + +## IP Multicast Address. +## +## Value: IP Address +## cluster.mcast.addr = 239.192.0.1 + +## Multicast Ports. +## +## Value: Port List +## cluster.mcast.ports = 4369,4370 + +## Multicast Iface. +## +## Value: Iface Address +## +## Default: 0.0.0.0 +## cluster.mcast.iface = 0.0.0.0 + +## Multicast Ttl. +## +## Value: 0-255 +## cluster.mcast.ttl = 255 + +## Multicast loop. +## +## Value: on | off +## cluster.mcast.loop = on + +##-------------------------------------------------------------------- +## Cluster using DNS A records. + +## DNS name. +## +## Value: String +## cluster.dns.name = localhost + +## The App name is used to build 'node.name' with IP address. +## +## Value: String +## cluster.dns.app = emqx + +##-------------------------------------------------------------------- +## Cluster using etcd + +## Etcd server list, seperated by ','. +## +## Value: String +## cluster.etcd.server = http://127.0.0.1:2379 + +## The prefix helps build nodes path in etcd. Each node in the cluster +## will create a path in etcd: v2/keys/// +## +## Value: String +## cluster.etcd.prefix = emqxcl + +## The TTL for node's path in etcd. +## +## Value: Duration +## +## Default: 1m, 1 minute +## cluster.etcd.node_ttl = 1m + +## Path to a file containing the client's private PEM-encoded key. +## +## Value: File +## cluster.etcd.ssl.keyfile = {{ platform_etc_dir }}/certs/client-key.pem + +## The path to a file containing the client's certificate. +## +## Value: File +## cluster.etcd.ssl.certfile = {{ platform_etc_dir }}/certs/client.pem + +## Path to the file containing PEM-encoded CA certificates. The CA certificates +## are used during server authentication and when building the client certificate chain. +## +## Value: File +## cluster.etcd.ssl.cacertfile = {{ platform_etc_dir }}/certs/ca.pem + +##-------------------------------------------------------------------- +## Cluster using Kubernetes + +## Kubernetes API server list, seperated by ','. +## +## Value: String +## cluster.k8s.apiserver = http://10.110.111.204:8080 + +## The service name helps lookup EMQ nodes in the cluster. +## +## Value: String +## cluster.k8s.service_name = emqx + +## The address type is used to extract host from k8s service. +## +## Value: ip | dns | hostname +## cluster.k8s.address_type = ip + +## The app name helps build 'node.name'. +## +## Value: String +## cluster.k8s.app_name = emqx + +## The suffix added to dns and hostname get from k8s service +## +## Value: String +## cluster.k8s.suffix = pod.cluster.local + +## Kubernetes Namespace +## +## Value: String +## cluster.k8s.namespace = default \ No newline at end of file diff --git a/etc/emqx.conf b/etc/emqx.conf index f6ded1032..58cf05cbe 100644 --- a/etc/emqx.conf +++ b/etc/emqx.conf @@ -2,176 +2,12 @@ ## EMQ X Configuration R4.0 ##==================================================================== -##-------------------------------------------------------------------- -## Cluster -##-------------------------------------------------------------------- - -## Cluster name. -## -## Value: String -cluster.name = emqxcl - -## Specify the erlang distributed protocol. -## -## Value: Enum -## - inet_tcp: the default; handles TCP streams with IPv4 addressing. -## - inet6_tcp: handles TCP with IPv6 addressing. -## - inet_tls: using TLS for Erlang Distribution. -## -## vm.args: -proto_dist inet_tcp -cluster.proto_dist = inet_tcp - -## Cluster auto-discovery strategy. -## -## Value: Enum -## - manual: Manual join command -## - static: Static node list -## - mcast: IP Multicast -## - dns: DNS A Record -## - etcd: etcd -## - k8s: Kubernetes -## -## Default: manual -cluster.discovery = manual - -## Enable cluster autoheal from network partition. -## -## Value: on | off -## -## Default: on -cluster.autoheal = on - -## Autoclean down node. A down node will be removed from the cluster -## if this value > 0. -## -## Value: Duration -## -h: hour, e.g. '2h' for 2 hours -## -m: minute, e.g. '5m' for 5 minutes -## -s: second, e.g. '30s' for 30 seconds -## -## Default: 5m -cluster.autoclean = 5m - -##-------------------------------------------------------------------- -## Cluster using static node list - -## Node list of the cluster. -## -## Value: String -## cluster.static.seeds = emqx1@127.0.0.1,emqx2@127.0.0.1 - -##-------------------------------------------------------------------- -## Cluster using IP Multicast. - -## IP Multicast Address. -## -## Value: IP Address -## cluster.mcast.addr = 239.192.0.1 - -## Multicast Ports. -## -## Value: Port List -## cluster.mcast.ports = 4369,4370 - -## Multicast Iface. -## -## Value: Iface Address -## -## Default: 0.0.0.0 -## cluster.mcast.iface = 0.0.0.0 - -## Multicast Ttl. -## -## Value: 0-255 -## cluster.mcast.ttl = 255 - -## Multicast loop. -## -## Value: on | off -## cluster.mcast.loop = on - -##-------------------------------------------------------------------- -## Cluster using DNS A records. - -## DNS name. -## -## Value: String -## cluster.dns.name = localhost - -## The App name is used to build 'node.name' with IP address. -## -## Value: String -## cluster.dns.app = emqx - -##-------------------------------------------------------------------- -## Cluster using etcd - -## Etcd server list, seperated by ','. -## -## Value: String -## cluster.etcd.server = http://127.0.0.1:2379 - -## The prefix helps build nodes path in etcd. Each node in the cluster -## will create a path in etcd: v2/keys/// -## -## Value: String -## cluster.etcd.prefix = emqxcl - -## The TTL for node's path in etcd. -## -## Value: Duration -## -## Default: 1m, 1 minute -## cluster.etcd.node_ttl = 1m - -## Path to a file containing the client's private PEM-encoded key. -## -## Value: File -## cluster.etcd.ssl.keyfile = {{ platform_etc_dir }}/certs/client-key.pem - -## The path to a file containing the client's certificate. -## -## Value: File -## cluster.etcd.ssl.certfile = {{ platform_etc_dir }}/certs/client.pem - -## Path to the file containing PEM-encoded CA certificates. The CA certificates -## are used during server authentication and when building the client certificate chain. -## -## Value: File -## cluster.etcd.ssl.cacertfile = {{ platform_etc_dir }}/certs/ca.pem - -##-------------------------------------------------------------------- -## Cluster using Kubernetes - -## Kubernetes API server list, seperated by ','. -## -## Value: String -## cluster.k8s.apiserver = http://10.110.111.204:8080 - -## The service name helps lookup EMQ nodes in the cluster. -## -## Value: String -## cluster.k8s.service_name = emqx - -## The address type is used to extract host from k8s service. -## -## Value: ip | dns | hostname -## cluster.k8s.address_type = ip - -## The app name helps build 'node.name'. -## -## Value: String -## cluster.k8s.app_name = emqx - -## The suffix added to dns and hostname get from k8s service -## -## Value: String -## cluster.k8s.suffix = pod.cluster.local - -## Kubernetes Namespace -## -## Value: String -## cluster.k8s.namespace = default +include {{ platform_etc_dir }}/cluster.conf +include {{ platform_etc_dir }}/rpc.conf +include {{ platform_etc_dir }}/logger.conf +include {{ platform_etc_dir }}/zones.conf +include {{ platform_etc_dir }}/listeners.conf +include {{ platform_etc_dir }}/sys_mon.conf ##-------------------------------------------------------------------- ## Node @@ -301,276 +137,6 @@ node.crash_dump = {{ platform_log_dir }}/crash.dump node.dist_listen_min = 6369 node.dist_listen_max = 6369 -##-------------------------------------------------------------------- -## RPC -##-------------------------------------------------------------------- -## RPC Mode. -## -## Value: sync | async -rpc.mode = async - -## Max batch size of async RPC requests. -## -## Value: Integer -## Zero or negative value disables rpc batching. -## -## NOTE: RPC batch won't work when rpc.mode = sync -rpc.async_batch_size = 256 - -## RPC port discovery -## -## The strategy for discovering the RPC listening port of other nodes. -## -## Value: Enum -## - manual: discover ports by `tcp_server_port` and `tcp_client_port`. -## - stateless: discover ports in a stateless manner. -## If node name is `emqx@127.0.0.1`, where the `` is an integer, -## then the listening port will be `5370 + ` -## -## Defaults to `stateless`. -rpc.port_discovery = stateless - -## TCP server port for RPC. -## -## Only takes effect when `rpc.port_discovery` = `manual`. -## -## Value: Port [1024-65535] -#rpc.tcp_server_port = 5369 - -## TCP port for outgoing RPC connections. -## -## Only takes effect when `rpc.port_discovery` = `manual`. -## -## Value: Port [1024-65535] -#rpc.tcp_client_port = 5369 - -## Number of outgoing RPC connections. -## -## Value: Interger [1-256] -## Defaults to NumberOfCPUSchedulers / 2 -#rpc.tcp_client_num = 1 - -## RCP Client connect timeout. -## -## Value: Seconds -rpc.connect_timeout = 5s - -## TCP send timeout of RPC client and server. -## -## Value: Seconds -rpc.send_timeout = 5s - -## Authentication timeout -## -## Value: Seconds -rpc.authentication_timeout = 5s - -## Default receive timeout for call() functions -## -## Value: Seconds -rpc.call_receive_timeout = 15s - -## Socket idle keepalive. -## -## Value: Seconds -rpc.socket_keepalive_idle = 900s - -## TCP Keepalive probes interval. -## -## Value: Seconds -rpc.socket_keepalive_interval = 75s - -## Probes lost to close the connection -## -## Value: Integer -rpc.socket_keepalive_count = 9 - -## Size of TCP send buffer. -## -## Value: Bytes -rpc.socket_sndbuf = 1MB - -## Size of TCP receive buffer. -## -## Value: Seconds -rpc.socket_recbuf = 1MB - -## Size of user-level software socket buffer. -## -## Value: Seconds -rpc.socket_buffer = 1MB - -##-------------------------------------------------------------------- -## Log -##-------------------------------------------------------------------- - -## Where to emit the logs. -## Enable the console (standard output) logs. -## -## Value: off | file | console | both -## - off: disable logs entirely -## - file: write logs only to file -## - console: write logs only to standard I/O -## - both: write logs both to file and standard I/O -log.to = both - -## The log severity level. -## -## Value: debug | info | notice | warning | error | critical | alert | emergency -## -## Note: Only the messages with severity level higher than or equal to -## this level will be logged. -## -## Default: warning -log.level = warning - -## The dir for log files. -## -## Value: Folder -log.dir = {{ platform_log_dir }} - -## The log filename for logs of level specified in "log.level". -## -## If `log.rotation` is enabled, this is the base name of the -## files. Each file in a rotated log is named .N, where N is an integer. -## -## Value: String -## Default: emqx.log -log.file = emqx.log - -## Limits the total number of characters printed for each log event. -## -## Value: Integer -## Default: No Limit -#log.chars_limit = 8192 - -## Enables the log rotation. -## With this enabled, new log files will be created when the current -## log file is full, max to `log.rotation.size` files will be created. -## -## Value: on | off -## Default: on -log.rotation = on - -## Maximum size of each log file. -## -## Value: Number -## Default: 10M -## Supported Unit: KB | MB | GB -log.rotation.size = 10MB - -## Maximum rotation count of log files. -## -## Value: Number -## Default: 5 -log.rotation.count = 5 - -## To create additional log files for specific log levels. -## -## Value: File Name -## Format: log.$level.file = $filename, -## where "$level" can be one of: debug, info, notice, warning, -## error, critical, alert, emergency -## Note: Log files for a specific log level will only contain all the logs -## that higher than or equal to that level -## -#log.info.file = info.log -#log.error.file = error.log - -## The max allowed queue length before switching to sync mode. -## -## Log overload protection parameter. If the message queue grows -## larger than this value the handler switches from anync to sync mode. -## -## Default: 100 -## -#log.sync_mode_qlen = 100 - -## The max allowed queue length before switching to drop mode. -## -## Log overload protection parameter. When the message queue grows -## larger than this threshold, the handler switches to a mode in which -## it drops all new events that senders want to log. -## -## Default: 3000 -## -#log.drop_mode_qlen = 3000 - -## The max allowed queue length before switching to flush mode. -## -## Log overload protection parameter. If the length of the message queue -## grows larger than this threshold, a flush (delete) operation takes place. -## To flush events, the handler discards the messages in the message queue -## by receiving them in a loop without logging. -## -## Default: 8000 -## -#log.flush_qlen = 8000 - -## Kill the log handler when it gets overloaded. -## -## Log overload protection parameter. It is possible that a handler, -## even if it can successfully manage peaks of high load without crashing, -## can build up a large message queue, or use a large amount of memory. -## We could kill the log handler in these cases and restart it after a -## few seconds. -## -## Default: on -## -#log.overload_kill = on - -## The max allowed queue length before killing the log hanlder. -## -## Log overload protection parameter. This is the maximum allowed queue -## length. If the message queue grows larger than this, the handler -## process is terminated. -## -## Default: 20000 -## -#log.overload_kill_qlen = 20000 - -## The max allowed memory size before killing the log hanlder. -## -## Log overload protection parameter. This is the maximum memory size -## that the handler process is allowed to use. If the handler grows -## larger than this, the process is terminated. -## -## Default: 30MB -## -#log.overload_kill_mem_size = 30MB - -## Restart the log hanlder after some seconds. -## -## Log overload protection parameter. If the handler is terminated, -## it restarts automatically after a delay specified in seconds. -## The value "infinity" prevents restarts. -## -## Default: 5s -## -#log.overload_kill_restart_after = 5s - -## Max burst count and time window for burst control. -## -## Log overload protection parameter. Large bursts of log events - many -## events received by the handler under a short period of time - can -## potentially cause problems. By specifying the maximum number of events -## to be handled within a certain time frame, the handler can avoid -## choking the log with massive amounts of printouts. -## -## This config controls the maximum number of events to handle within -## a time frame. After the limit is reached, successive events are -## dropped until the end of the time frame. -## -## Note that there would be no warning if any messages were -## dropped because of burst control. -## -## Comment this config out to disable the burst control feature. -## -## Value: MaxBurstCount,TimeWindow -## Default: disabled -## -#log.burst_limit = 20000, 1s - ##-------------------------------------------------------------------- ## Authentication/Access Control ##-------------------------------------------------------------------- @@ -586,11 +152,6 @@ allow_anonymous = true ## Value: allow | deny acl_nomatch = allow -## Default ACL File. -## -## Value: File Name -acl_file = {{ platform_etc_dir }}/acl.conf - ## Whether to enable ACL cache. ## ## If enabled, ACLs roles for each client will be cached in the memory @@ -685,1340 +246,6 @@ mqtt.strict_mode = false ## Value: String ## mqtt.response_information = example -##-------------------------------------------------------------------- -## Zones -##-------------------------------------------------------------------- - -##-------------------------------------------------------------------- -## External Zone - -## Idle timeout of the external MQTT connections. -## -## Value: duration -zone.external.idle_timeout = 15s - -## Enable ACL check. -## -## Value: Flag -zone.external.enable_acl = on - -## Enable ban check. -## -## Value: Flag -zone.external.enable_ban = on - -## Enable per connection statistics. -## -## Value: on | off -zone.external.enable_stats = on - -## The action when acl check reject current operation -## -## Value: ignore | disconnect -## Default: ignore -zone.external.acl_deny_action = ignore - -## Force the MQTT connection process GC after this number of -## messages | bytes passed through. -## -## Numbers delimited by `|'. Zero or negative is to disable. -zone.external.force_gc_policy = 16000|16MB - -## Max message queue length and total heap size to force shutdown -## connection/session process. -## Message queue here is the Erlang process mailbox, but not the number -## of queued MQTT messages of QoS 1 and 2. -## -## Numbers delimited by `|'. Zero or negative is to disable. -## zone.external.force_shutdown_policy = 32000|32MB - -## Maximum MQTT packet size allowed. -## -## Value: Bytes -## Default: 1MB -## zone.external.max_packet_size = 64KB - -## Maximum length of MQTT clientId allowed. -## -## Value: Number [23-65535] -## zone.external.max_clientid_len = 1024 - -## Maximum topic levels allowed. 0 means no limit. -## -## Value: Number -## zone.external.max_topic_levels = 7 - -## Maximum QoS allowed. -## -## Value: 0 | 1 | 2 -## zone.external.max_qos_allowed = 2 - -## Maximum Topic Alias, 0 means no limit. -## -## Value: 0-65535 -## zone.external.max_topic_alias = 65535 - -## Whether the Server supports retained messages. -## -## Value: boolean -## zone.external.retain_available = true - -## Whether the Server supports Wildcard Subscriptions -## -## Value: boolean -## zone.external.wildcard_subscription = false - -## Whether the Server supports Shared Subscriptions -## -## Value: boolean -## zone.external.shared_subscription = false - -## Server Keep Alive -## -## Value: Number -## zone.external.server_keepalive = 0 - -## The backoff for MQTT keepalive timeout. The broker will kick a connection out -## until 'Keepalive * backoff * 2' timeout. -## -## Value: Float > 0.5 -zone.external.keepalive_backoff = 0.75 - -## Maximum number of subscriptions allowed, 0 means no limit. -## -## Value: Number -zone.external.max_subscriptions = 0 - -## Force to upgrade QoS according to subscription. -## -## Value: on | off -zone.external.upgrade_qos = off - -## Maximum size of the Inflight Window storing QoS1/2 messages delivered but unacked. -## -## Value: Number -zone.external.max_inflight = 32 - -## Retry interval for QoS1/2 message delivering. -## -## Value: Duration -zone.external.retry_interval = 30s - -## Maximum QoS2 packets (Client -> Broker) awaiting PUBREL, 0 means no limit. -## -## Value: Number -zone.external.max_awaiting_rel = 100 - -## The QoS2 messages (Client -> Broker) will be dropped if awaiting PUBREL timeout. -## -## Value: Duration -zone.external.await_rel_timeout = 300s - -## Default session expiry interval for MQTT V3.1.1 connections. -## -## Value: Duration -## -d: day -## -h: hour -## -m: minute -## -s: second -## -## Default: 2h, 2 hours -zone.external.session_expiry_interval = 2h - -## Maximum queue length. Enqueued messages when persistent client disconnected, -## or inflight window is full. 0 means no limit. -## -## Value: Number >= 0 -zone.external.max_mqueue_len = 1000 - -## Topic priorities. -## 'none' to indicate no priority table (by default), hence all messages -## are treated equal -## -## Priority number [1-255] -## Example: topic/1=10,topic/2=8 -## NOTE: comma and equal signs are not allowed for priority topic names -## NOTE: messages for topics not in the priority table are treated as -## either highest or lowest priority depending on the configured -## value for mqueue_default_priority -## -zone.external.mqueue_priorities = none - -## Default to highest priority for topics not matching priority table -## -## Value: highest | lowest -zone.external.mqueue_default_priority = highest - -## Whether to enqueue QoS0 messages. -## -## Value: false | true -zone.external.mqueue_store_qos0 = true - -## Whether to turn on flapping detect -## -## Value: on | off -zone.external.enable_flapping_detect = off - -## Message limit for the a external MQTT connection. -## -## Value: Number,Duration -## Example: 100 messages per 10 seconds. -#zone.external.rate_limit.conn_messages_in = 100,10s - -## Bytes limit for a external MQTT connections. -## -## Value: Number,Duration -## Example: 100KB incoming per 10 seconds. -#zone.external.rate_limit.conn_bytes_in = 100KB,10s - -## Messages quota for the each of external MQTT connection. -## This value consumed by the number of recipient on a message. -## -## Value: Number, Duration -## -## Example: 100 messaegs per 1s -#zone.external.quota.conn_messages_routing = 100,1s - -## Messages quota for the all of external MQTT connections. -## This value consumed by the number of recipient on a message. -## -## Value: Number, Duration -## -## Example: 200000 messaegs per 1s -#zone.external.quota.overall_messages_routing = 200000,1s - -## All the topics will be prefixed with the mountpoint path if this option is enabled. -## -## Variables in mountpoint path: -## - %c: clientid -## - %u: username -## -## Value: String -## zone.external.mountpoint = devicebound/ - -## Whether use username replace client id -## -## Value: boolean -## Default: false -zone.external.use_username_as_clientid = false - -## Whether to ignore loop delivery of messages.(for mqtt v3.1.1) -## -## Value: true | false -zone.external.ignore_loop_deliver = false - -## Whether to parse the MQTT frame in strict mode -## -## Value: true | false -zone.external.strict_mode = false - -## Specify the response information returned to the client -## -## Value: String -## zone.external.response_information = example - -##-------------------------------------------------------------------- -## Internal Zone - -zone.internal.allow_anonymous = true - -## Enable per connection stats. -## -## Value: Flag -zone.internal.enable_stats = on - -## Enable ACL check. -## -## Value: Flag -zone.internal.enable_acl = off - -## The action when acl check reject current operation -## -## Value: ignore | disconnect -## Default: ignore -zone.internal.acl_deny_action = ignore - -## See zone.$name.force_gc_policy -## zone.internal.force_gc_policy = 128000|128MB - -## See zone.$name.wildcard_subscription. -## -## Value: boolean -## zone.internal.wildcard_subscription = true - -## See zone.$name.shared_subscription. -## -## Value: boolean -## zone.internal.shared_subscription = true - -## See zone.$name.max_subscriptions. -## -## Value: Integer -zone.internal.max_subscriptions = 0 - -## See zone.$name.max_inflight -## -## Value: Number -zone.internal.max_inflight = 128 - -## See zone.$name.max_awaiting_rel -## -## Value: Number -zone.internal.max_awaiting_rel = 1000 - -## See zone.$name.max_mqueue_len -## -## Value: Number >= 0 -zone.internal.max_mqueue_len = 10000 - -## Whether to enqueue Qos0 messages. -## -## Value: false | true -zone.internal.mqueue_store_qos0 = true - -## Whether to turn on flapping detect -## -## Value: on | off -zone.internal.enable_flapping_detect = off - -## See zone.$name.force_shutdown_policy -## zone.internal.force_shutdown_policy = 128000|128MB - -## All the topics will be prefixed with the mountpoint path if this option is enabled. -## -## Variables in mountpoint path: -## - %c: clientid -## - %u: username -## -## Value: String -## zone.internal.mountpoint = cloudbound/ - -## Whether to ignore loop delivery of messages.(for mqtt v3.1.1) -## -## Value: true | false -zone.internal.ignore_loop_deliver = false - -## Whether to parse the MQTT frame in strict mode -## -## Value: true | false -zone.internal.strict_mode = false - -## Specify the response information returned to the client -## -## Value: String -## zone.internal.response_information = example - -## Allow the zone's clients to bypass authentication step -## -## Value: true | false -zone.internal.bypass_auth_plugins = true - -##-------------------------------------------------------------------- -## Listeners -##-------------------------------------------------------------------- - -##-------------------------------------------------------------------- -## MQTT/TCP - External TCP Listener for MQTT Protocol - -## listener.tcp.$name is the IP address and port that the MQTT/TCP -## listener will bind. -## -## Value: IP:Port | Port -## -## Examples: 1883, 127.0.0.1:1883, ::1:1883 -listener.tcp.external = 0.0.0.0:1883 - -## The acceptor pool for external MQTT/TCP listener. -## -## Value: Number -listener.tcp.external.acceptors = 8 - -## Maximum number of concurrent MQTT/TCP connections. -## -## Value: Number -listener.tcp.external.max_connections = 1024000 - -## Maximum external connections per second. -## -## Value: Number -listener.tcp.external.max_conn_rate = 1000 - -## Specify the {active, N} option for the external MQTT/TCP Socket. -## -## Value: Number -listener.tcp.external.active_n = 100 - -## Zone of the external MQTT/TCP listener belonged to. -## -## See: zone.$name.* -## -## Value: String -listener.tcp.external.zone = external - -## The access control rules for the MQTT/TCP listener. -## -## See: https://github.com/emqtt/esockd#allowdeny -## -## Value: ACL Rule -## -## Example: allow 192.168.0.0/24 -listener.tcp.external.access.1 = allow all - -## Enable the Proxy Protocol V1/2 if the EMQ X cluster is deployed -## behind HAProxy or Nginx. -## -## See: https://www.haproxy.com/blog/haproxy/proxy-protocol/ -## -## Value: on | off -## listener.tcp.external.proxy_protocol = on - -## Sets the timeout for proxy protocol. EMQ X will close the TCP connection -## if no proxy protocol packet recevied within the timeout. -## -## Value: Duration -## listener.tcp.external.proxy_protocol_timeout = 3s - -## Enable the option for X.509 certificate based authentication. -## EMQX will use the common name of certificate as MQTT username. -## -## Value: cn | dn | crt -## listener.tcp.external.peer_cert_as_username = cn - -## The TCP backlog defines the maximum length that the queue of pending -## connections can grow to. -## -## Value: Number >= 0 -listener.tcp.external.backlog = 1024 - -## The TCP send timeout for external MQTT connections. -## -## Value: Duration -listener.tcp.external.send_timeout = 15s - -## Close the TCP connection if send timeout. -## -## Value: on | off -listener.tcp.external.send_timeout_close = on - -## The TCP receive buffer(os kernel) for MQTT connections. -## -## See: http://erlang.org/doc/man/inet.html -## -## Value: Bytes -## listener.tcp.external.recbuf = 2KB - -## The TCP send buffer(os kernel) for MQTT connections. -## -## See: http://erlang.org/doc/man/inet.html -## -## Value: Bytes -## listener.tcp.external.sndbuf = 2KB - -## The size of the user-level software buffer used by the driver. -## Not to be confused with options sndbuf and recbuf, which correspond -## to the Kernel socket buffers. It is recommended to have val(buffer) -## >= max(val(sndbuf),val(recbuf)) to avoid performance issues because -## of unnecessary copying. val(buffer) is automatically set to the above -## maximum when values sndbuf or recbuf are set. -## -## See: http://erlang.org/doc/man/inet.html -## -## Value: Bytes -## listener.tcp.external.buffer = 2KB - -## Sets the 'buffer = max(sndbuf, recbuf)' if this option is enabled. -## -## Value: on | off -## listener.tcp.external.tune_buffer = off - -## The TCP_NODELAY flag for MQTT connections. Small amounts of data are -## sent immediately if the option is enabled. -## -## Value: true | false -listener.tcp.external.nodelay = true - -## The SO_REUSEADDR flag for TCP listener. -## -## Value: true | false -listener.tcp.external.reuseaddr = true - -##-------------------------------------------------------------------- -## Internal TCP Listener for MQTT Protocol - -## The IP address and port that the internal MQTT/TCP protocol listener -## will bind. -## -## Value: IP:Port, Port -## -## Examples: 11883, 127.0.0.1:11883, ::1:11883 -listener.tcp.internal = 127.0.0.1:11883 - -## The acceptor pool for internal MQTT/TCP listener. -## -## Value: Number -listener.tcp.internal.acceptors = 4 - -## Maximum number of concurrent MQTT/TCP connections. -## -## Value: Number -listener.tcp.internal.max_connections = 1024000 - -## Maximum internal connections per second. -## -## Value: Number -listener.tcp.internal.max_conn_rate = 1000 - -## Specify the {active, N} option for the internal MQTT/TCP Socket. -## -## Value: Number -listener.tcp.internal.active_n = 1000 - -## Zone of the internal MQTT/TCP listener belonged to. -## -## Value: String -listener.tcp.internal.zone = internal - -## The TCP backlog of internal MQTT/TCP Listener. -## -## See: listener.tcp.$name.backlog -## -## Value: Number >= 0 -listener.tcp.internal.backlog = 512 - -## The TCP send timeout for internal MQTT connections. -## -## See: listener.tcp.$name.send_timeout -## -## Value: Duration -listener.tcp.internal.send_timeout = 5s - -## Close the MQTT/TCP connection if send timeout. -## -## See: listener.tcp.$name.send_timeout_close -## -## Value: on | off -listener.tcp.internal.send_timeout_close = on - -## The TCP receive buffer(os kernel) for internal MQTT connections. -## -## See: listener.tcp.$name.recbuf -## -## Value: Bytes -listener.tcp.internal.recbuf = 64KB - -## The TCP send buffer(os kernel) for internal MQTT connections. -## -## See: http://erlang.org/doc/man/inet.html -## -## Value: Bytes -listener.tcp.internal.sndbuf = 64KB - -## The size of the user-level software buffer used by the driver. -## -## See: listener.tcp.$name.buffer -## -## Value: Bytes -## listener.tcp.internal.buffer = 16KB - -## Sets the 'buffer = max(sndbuf, recbuf)' if this option is enabled. -## -## See: listener.tcp.$name.tune_buffer -## -## Value: on | off -## listener.tcp.internal.tune_buffer = off - -## The TCP_NODELAY flag for internal MQTT connections. -## -## See: listener.tcp.$name.nodelay -## -## Value: true | false -listener.tcp.internal.nodelay = false - -## The SO_REUSEADDR flag for MQTT/TCP Listener. -## -## Value: true | false -listener.tcp.internal.reuseaddr = true - -##-------------------------------------------------------------------- -## MQTT/SSL - External SSL Listener for MQTT Protocol - -## listener.ssl.$name is the IP address and port that the MQTT/SSL -## listener will bind. -## -## Value: IP:Port | Port -## -## Examples: 8883, 127.0.0.1:8883, ::1:8883 -listener.ssl.external = 8883 - -## The acceptor pool for external MQTT/SSL listener. -## -## Value: Number -listener.ssl.external.acceptors = 16 - -## Maximum number of concurrent MQTT/SSL connections. -## -## Value: Number -listener.ssl.external.max_connections = 102400 - -## Maximum MQTT/SSL connections per second. -## -## Value: Number -listener.ssl.external.max_conn_rate = 500 - -## Specify the {active, N} option for the internal MQTT/SSL Socket. -## -## Value: Number -listener.ssl.external.active_n = 100 - -## Zone of the external MQTT/SSL listener belonged to. -## -## Value: String -listener.ssl.external.zone = external - -## The access control rules for the MQTT/SSL listener. -## -## See: listener.tcp.$name.access -## -## Value: ACL Rule -listener.ssl.external.access.1 = allow all - -## Enable the Proxy Protocol V1/2 if the EMQ cluster is deployed behind -## HAProxy or Nginx. -## -## See: listener.tcp.$name.proxy_protocol -## -## Value: on | off -## listener.ssl.external.proxy_protocol = on - -## Sets the timeout for proxy protocol. -## -## See: listener.tcp.$name.proxy_protocol_timeout -## -## Value: Duration -## listener.ssl.external.proxy_protocol_timeout = 3s - -## TLS versions only to protect from POODLE attack. -## -## See: http://erlang.org/doc/man/ssl.html -## -## Value: String, seperated by ',' -## listener.ssl.external.tls_versions = tlsv1.2,tlsv1.1,tlsv1 - -## TLS Handshake timeout. -## -## Value: Duration -listener.ssl.external.handshake_timeout = 15s - -## Path to the file containing the user's private PEM-encoded key. -## -## See: http://erlang.org/doc/man/ssl.html -## -## Value: File -listener.ssl.external.keyfile = {{ platform_etc_dir }}/certs/key.pem - -## Path to a file containing the user certificate. -## -## See: http://erlang.org/doc/man/ssl.html -## -## Value: File -listener.ssl.external.certfile = {{ platform_etc_dir }}/certs/cert.pem - -## Path to the file containing PEM-encoded CA certificates. The CA certificates -## are used during server authentication and when building the client certificate chain. -## -## Value: File -## listener.ssl.external.cacertfile = {{ platform_etc_dir }}/certs/cacert.pem - -## The Ephemeral Diffie-Helman key exchange is a very effective way of -## ensuring Forward Secrecy by exchanging a set of keys that never hit -## the wire. Since the DH key is effectively signed by the private key, -## it needs to be at least as strong as the private key. In addition, -## the default DH groups that most of the OpenSSL installations have -## are only a handful (since they are distributed with the OpenSSL -## package that has been built for the operating system it’s running on) -## and hence predictable (not to mention, 1024 bits only). -## In order to escape this situation, first we need to generate a fresh, -## strong DH group, store it in a file and then use the option above, -## to force our SSL application to use the new DH group. Fortunately, -## OpenSSL provides us with a tool to do that. Simply run: -## openssl dhparam -out dh-params.pem 2048 -## -## Value: File -## listener.ssl.external.dhfile = {{ platform_etc_dir }}/certs/dh-params.pem - -## A server only does x509-path validation in mode verify_peer, -## as it then sends a certificate request to the client (this -## message is not sent if the verify option is verify_none). -## You can then also want to specify option fail_if_no_peer_cert. -## More information at: http://erlang.org/doc/man/ssl.html -## -## Value: verify_peer | verify_none -## listener.ssl.external.verify = verify_peer - -## Used together with {verify, verify_peer} by an SSL server. If set to true, -## the server fails if the client does not have a certificate to send, that is, -## sends an empty certificate. -## -## Value: true | false -## listener.ssl.external.fail_if_no_peer_cert = true - -## This is the single most important configuration option of an Erlang SSL -## application. Ciphers (and their ordering) define the way the client and -## server encrypt information over the wire, from the initial Diffie-Helman -## key exchange, the session key encryption ## algorithm and the message -## digest algorithm. Selecting a good cipher suite is critical for the -## application’s data security, confidentiality and performance. -## -## The cipher list above offers: -## -## A good balance between compatibility with older browsers. -## It can get stricter for Machine-To-Machine scenarios. -## Perfect Forward Secrecy. -## No old/insecure encryption and HMAC algorithms -## -## Most of it was copied from Mozilla’s Server Side TLS article -## -## Value: Ciphers -listener.ssl.external.ciphers = ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES256-SHA384,ECDHE-RSA-AES256-SHA384,ECDHE-ECDSA-DES-CBC3-SHA,ECDH-ECDSA-AES256-GCM-SHA384,ECDH-RSA-AES256-GCM-SHA384,ECDH-ECDSA-AES256-SHA384,ECDH-RSA-AES256-SHA384,DHE-DSS-AES256-GCM-SHA384,DHE-DSS-AES256-SHA256,AES256-GCM-SHA384,AES256-SHA256,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES128-SHA256,ECDHE-RSA-AES128-SHA256,ECDH-ECDSA-AES128-GCM-SHA256,ECDH-RSA-AES128-GCM-SHA256,ECDH-ECDSA-AES128-SHA256,ECDH-RSA-AES128-SHA256,DHE-DSS-AES128-GCM-SHA256,DHE-DSS-AES128-SHA256,AES128-GCM-SHA256,AES128-SHA256,ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,DHE-DSS-AES256-SHA,ECDH-ECDSA-AES256-SHA,ECDH-RSA-AES256-SHA,AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,DHE-DSS-AES128-SHA,ECDH-ECDSA-AES128-SHA,ECDH-RSA-AES128-SHA,AES128-SHA - -## Ciphers for TLS PSK. -## Note that 'listener.ssl.external.ciphers' and 'listener.ssl.external.psk_ciphers' cannot -## be configured at the same time. -## See 'https://tools.ietf.org/html/rfc4279#section-2'. -#listener.ssl.external.psk_ciphers = PSK-AES128-CBC-SHA,PSK-AES256-CBC-SHA,PSK-3DES-EDE-CBC-SHA,PSK-RC4-SHA - -## SSL parameter renegotiation is a feature that allows a client and a server -## to renegotiate the parameters of the SSL connection on the fly. -## RFC 5746 defines a more secure way of doing this. By enabling secure renegotiation, -## you drop support for the insecure renegotiation, prone to MitM attacks. -## -## Value: on | off -## listener.ssl.external.secure_renegotiate = off - -## A performance optimization setting, it allows clients to reuse -## pre-existing sessions, instead of initializing new ones. -## Read more about it here. -## -## See: http://erlang.org/doc/man/ssl.html -## -## Value: on | off -## listener.ssl.external.reuse_sessions = on - -## An important security setting, it forces the cipher to be set based -## on the server-specified order instead of the client-specified order, -## hence enforcing the (usually more properly configured) security -## ordering of the server administrator. -## -## Value: on | off -## listener.ssl.external.honor_cipher_order = on - -## Use the CN, DN or CRT field from the client certificate as a username. -## Notice that 'verify' should be set as 'verify_peer'. -## -## Value: cn | dn | crt -## listener.ssl.external.peer_cert_as_username = cn - -## TCP backlog for the SSL connection. -## -## See listener.tcp.$name.backlog -## -## Value: Number >= 0 -## listener.ssl.external.backlog = 1024 - -## The TCP send timeout for the SSL connection. -## -## See listener.tcp.$name.send_timeout -## -## Value: Duration -## listener.ssl.external.send_timeout = 15s - -## Close the SSL connection if send timeout. -## -## See: listener.tcp.$name.send_timeout_close -## -## Value: on | off -## listener.ssl.external.send_timeout_close = on - -## The TCP receive buffer(os kernel) for the SSL connections. -## -## See: listener.tcp.$name.recbuf -## -## Value: Bytes -## listener.ssl.external.recbuf = 4KB - -## The TCP send buffer(os kernel) for internal MQTT connections. -## -## See: listener.tcp.$name.sndbuf -## -## Value: Bytes -## listener.ssl.external.sndbuf = 4KB - -## The size of the user-level software buffer used by the driver. -## -## See: listener.tcp.$name.buffer -## -## Value: Bytes -## listener.ssl.external.buffer = 4KB - -## Sets the 'buffer = max(sndbuf, recbuf)' if this option is enabled. -## -## See: listener.tcp.$name.tune_buffer -## -## Value: on | off -## listener.ssl.external.tune_buffer = off - -## The TCP_NODELAY flag for SSL connections. -## -## See: listener.tcp.$name.nodelay -## -## Value: true | false -## listener.ssl.external.nodelay = true - -## The SO_REUSEADDR flag for MQTT/SSL Listener. -## -## Value: true | false -listener.ssl.external.reuseaddr = true - -##-------------------------------------------------------------------- -## External WebSocket listener for MQTT protocol - -## listener.ws.$name is the IP address and port that the MQTT/WebSocket -## listener will bind. -## -## Value: IP:Port | Port -## -## Examples: 8083, 127.0.0.1:8083, ::1:8083 -listener.ws.external = 8083 - -## The path of WebSocket MQTT endpoint -## -## Value: URL Path -listener.ws.external.mqtt_path = /mqtt - -## The acceptor pool for external MQTT/WebSocket listener. -## -## Value: Number -listener.ws.external.acceptors = 4 - -## Maximum number of concurrent MQTT/WebSocket connections. -## -## Value: Number -listener.ws.external.max_connections = 102400 - -## Maximum MQTT/WebSocket connections per second. -## -## Value: Number -listener.ws.external.max_conn_rate = 1000 - -## Simulate the {active, N} option for the MQTT/WebSocket connections. -## -## Value: Number -listener.ws.external.active_n = 100 - -## Zone of the external MQTT/WebSocket listener belonged to. -## -## Value: String -listener.ws.external.zone = external - -## The access control for the MQTT/WebSocket listener. -## -## See: listener.ws.$name.access -## -## Value: ACL Rule -listener.ws.external.access.1 = allow all - -## Verify if the protocol header is valid. Turn off for WeChat MiniApp. -## -## Value: on | off -listener.ws.external.verify_protocol_header = on - -## Enable the Proxy Protocol V1/2 if the EMQ cluster is deployed behind -## HAProxy or Nginx. -## -## See: listener.ws.$name.proxy_protocol -## -## Value: on | off -## listener.ws.external.proxy_protocol = on - -## Sets the timeout for proxy protocol. -## -## See: listener.ws.$name.proxy_protocol_timeout -## -## Value: Duration -## listener.ws.external.proxy_protocol_timeout = 3s - -## The TCP backlog of external MQTT/WebSocket Listener. -## -## See: listener.ws.$name.backlog -## -## Value: Number >= 0 -listener.ws.external.backlog = 1024 - -## The TCP send timeout for external MQTT/WebSocket connections. -## -## See: listener.ws.$name.send_timeout -## -## Value: Duration -listener.ws.external.send_timeout = 15s - -## Close the MQTT/WebSocket connection if send timeout. -## -## See: listener.ws.$name.send_timeout_close -## -## Value: on | off -listener.ws.external.send_timeout_close = on - -## The TCP receive buffer(os kernel) for external MQTT/WebSocket connections. -## -## See: listener.ws.$name.recbuf -## -## Value: Bytes -## listener.ws.external.recbuf = 2KB - -## The TCP send buffer(os kernel) for external MQTT/WebSocket connections. -## -## See: listener.ws.$name.sndbuf -## -## Value: Bytes -## listener.ws.external.sndbuf = 2KB - -## The size of the user-level software buffer used by the driver. -## -## See: listener.ws.$name.buffer -## -## Value: Bytes -## listener.ws.external.buffer = 2KB - -## Sets the 'buffer = max(sndbuf, recbuf)' if this option is enabled. -## -## See: listener.ws.$name.tune_buffer -## -## Value: on | off -## listener.ws.external.tune_buffer = off - -## The TCP_NODELAY flag for external MQTT/WebSocket connections. -## -## See: listener.ws.$name.nodelay -## -## Value: true | false -listener.ws.external.nodelay = true - -## The compress flag for external MQTT/WebSocket connections. -## -## If this Value is set true,the websocket message would be compressed -## -## Value: true | false -## listener.ws.external.compress = true - -## The level of deflate options for external MQTT/WebSocket connections. -## -## See: listener.ws.$name.deflate_opts.level -## -## Value: none | default | best_compression | best_speed -## listener.ws.external.deflate_opts.level = default - -## The mem_level of deflate options for external MQTT/WebSocket connections. -## -## See: listener.ws.$name.deflate_opts.mem_level -## -## Valid range is 1-9 -## listener.ws.external.deflate_opts.mem_level = 8 - -## The strategy of deflate options for external MQTT/WebSocket connections. -## -## See: listener.ws.$name.deflate_opts.strategy -## -## Value: default | filtered | huffman_only | rle -## listener.ws.external.deflate_opts.strategy = default - -## The deflate option for external MQTT/WebSocket connections. -## -## See: listener.ws.$name.deflate_opts.server_context_takeover -## -## Value: takeover | no_takeover -## listener.ws.external.deflate_opts.server_context_takeover = takeover - -## The deflate option for external MQTT/WebSocket connections. -## -## See: listener.ws.$name.deflate_opts.client_context_takeover -## -## Value: takeover | no_takeover -## listener.ws.external.deflate_opts.client_context_takeover = takeover - -## The deflate options for external MQTT/WebSocket connections. -## -## See: listener.ws.$name.deflate_opts.server_max_window_bits -## -## Valid range is 8-15 -## listener.ws.external.deflate_opts.server_max_window_bits = 15 - -## The deflate options for external MQTT/WebSocket connections. -## -## See: listener.ws.$name.deflate_opts.client_max_window_bits -## -## Valid range is 8-15 -## listener.ws.external.deflate_opts.client_max_window_bits = 15 - -## The idle timeout for external MQTT/WebSocket connections. -## -## See: listener.ws.$name.idle_timeout -## -## Value: Duration -## listener.ws.external.idle_timeout = 60s - -## The max frame size for external MQTT/WebSocket connections. -## -## -## Value: Number -## listener.ws.external.max_frame_size = 0 - -## Whether a WebSocket message is allowed to contain multiple MQTT packets -## -## Value: single | multiple -listener.ws.external.mqtt_piggyback = multiple - -##-------------------------------------------------------------------- -## External WebSocket/SSL listener for MQTT Protocol - -## listener.wss.$name is the IP address and port that the MQTT/WebSocket/SSL -## listener will bind. -## -## Value: IP:Port | Port -## -## Examples: 8084, 127.0.0.1:8084, ::1:8084 -listener.wss.external = 8084 - -## The path of WebSocket MQTT endpoint -## -## Value: URL Path -listener.wss.external.mqtt_path = /mqtt - -## The acceptor pool for external MQTT/WebSocket/SSL listener. -## -## Value: Number -listener.wss.external.acceptors = 4 - -## Maximum number of concurrent MQTT/Webwocket/SSL connections. -## -## Value: Number -listener.wss.external.max_connections = 16 - -## Maximum MQTT/WebSocket/SSL connections per second. -## -## See: listener.tcp.$name.max_conn_rate -## -## Value: Number -listener.wss.external.max_conn_rate = 1000 - -## Simulate the {active, N} option for the MQTT/WebSocket/SSL connections. -## -## Value: Number -listener.wss.external.active_n = 100 - -## Zone of the external MQTT/WebSocket/SSL listener belonged to. -## -## Value: String -listener.wss.external.zone = external - -## The access control rules for the MQTT/WebSocket/SSL listener. -## -## See: listener.tcp.$name.access. -## -## Value: ACL Rule -listener.wss.external.access.1 = allow all - -## See: listener.ws.external.verify_protocol_header -## -## Value: on | off -listener.wss.external.verify_protocol_header = on - -## Enable the Proxy Protocol V1/2 support. -## -## See: listener.tcp.$name.proxy_protocol -## -## Value: on | off -## listener.wss.external.proxy_protocol = on - -## Sets the timeout for proxy protocol. -## -## See: listener.tcp.$name.proxy_protocol_timeout -## -## Value: Duration -## listener.wss.external.proxy_protocol_timeout = 3s - -## TLS versions only to protect from POODLE attack. -## -## See: listener.ssl.$name.tls_versions -## -## Value: String, seperated by ',' -## listener.wss.external.tls_versions = tlsv1.2,tlsv1.1,tlsv1 - -## Path to the file containing the user's private PEM-encoded key. -## -## See: listener.ssl.$name.keyfile -## -## Value: File -listener.wss.external.keyfile = {{ platform_etc_dir }}/certs/key.pem - -## Path to a file containing the user certificate. -## -## See: listener.ssl.$name.certfile -## -## Value: File -listener.wss.external.certfile = {{ platform_etc_dir }}/certs/cert.pem - -## Path to the file containing PEM-encoded CA certificates. -## -## See: listener.ssl.$name.cacert -## -## Value: File -## listener.wss.external.cacertfile = {{ platform_etc_dir }}/certs/cacert.pem - -## See: listener.ssl.$name.dhfile -## -## Value: File -## listener.ssl.external.dhfile = {{ platform_etc_dir }}/certs/dh-params.pem - -## See: listener.ssl.$name.vefify -## -## Value: vefify_peer | verify_none -## listener.wss.external.verify = verify_peer - -## See: listener.ssl.$name.fail_if_no_peer_cert -## -## Value: false | true -## listener.wss.external.fail_if_no_peer_cert = true - -## See: listener.ssl.$name.ciphers -## -## Value: Ciphers -listener.wss.external.ciphers = ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES256-SHA384,ECDHE-RSA-AES256-SHA384,ECDHE-ECDSA-DES-CBC3-SHA,ECDH-ECDSA-AES256-GCM-SHA384,ECDH-RSA-AES256-GCM-SHA384,ECDH-ECDSA-AES256-SHA384,ECDH-RSA-AES256-SHA384,DHE-DSS-AES256-GCM-SHA384,DHE-DSS-AES256-SHA256,AES256-GCM-SHA384,AES256-SHA256,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES128-SHA256,ECDHE-RSA-AES128-SHA256,ECDH-ECDSA-AES128-GCM-SHA256,ECDH-RSA-AES128-GCM-SHA256,ECDH-ECDSA-AES128-SHA256,ECDH-RSA-AES128-SHA256,DHE-DSS-AES128-GCM-SHA256,DHE-DSS-AES128-SHA256,AES128-GCM-SHA256,AES128-SHA256,ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,DHE-DSS-AES256-SHA,ECDH-ECDSA-AES256-SHA,ECDH-RSA-AES256-SHA,AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,DHE-DSS-AES128-SHA,ECDH-ECDSA-AES128-SHA,ECDH-RSA-AES128-SHA,AES128-SHA - -## Ciphers for TLS PSK. -## Note that 'listener.wss.external.ciphers' and 'listener.wss.external.psk_ciphers' cannot -## be configured at the same time. -## See 'https://tools.ietf.org/html/rfc4279#section-2'. -## listener.wss.external.psk_ciphers = PSK-AES128-CBC-SHA,PSK-AES256-CBC-SHA,PSK-3DES-EDE-CBC-SHA,PSK-RC4-SHA - -## See: listener.ssl.$name.secure_renegotiate -## -## Value: on | off -## listener.wss.external.secure_renegotiate = off - -## See: listener.ssl.$name.reuse_sessions -## -## Value: on | off -## listener.wss.external.reuse_sessions = on - -## See: listener.ssl.$name.honor_cipher_order -## -## Value: on | off -## listener.wss.external.honor_cipher_order = on - -## See: listener.ssl.$name.peer_cert_as_username -## -## Value: cn | dn | crt -## listener.wss.external.peer_cert_as_username = cn - -## TCP backlog for the WebSocket/SSL connection. -## -## See: listener.tcp.$name.backlog -## -## Value: Number >= 0 -listener.wss.external.backlog = 1024 - -## The TCP send timeout for the WebSocket/SSL connection. -## -## See: listener.tcp.$name.send_timeout -## -## Value: Duration -listener.wss.external.send_timeout = 15s - -## Close the WebSocket/SSL connection if send timeout. -## -## See: listener.tcp.$name.send_timeout_close -## -## Value: on | off -listener.wss.external.send_timeout_close = on - -## The TCP receive buffer(os kernel) for the WebSocket/SSL connections. -## -## See: listener.tcp.$name.recbuf -## -## Value: Bytes -## listener.wss.external.recbuf = 4KB - -## The TCP send buffer(os kernel) for the WebSocket/SSL connections. -## -## See: listener.tcp.$name.sndbuf -## -## Value: Bytes -## listener.wss.external.sndbuf = 4KB - -## The size of the user-level software buffer used by the driver. -## -## See: listener.tcp.$name.buffer -## -## Value: Bytes -## listener.wss.external.buffer = 4KB - -## The TCP_NODELAY flag for WebSocket/SSL connections. -## -## See: listener.tcp.$name.nodelay -## -## Value: true | false -## listener.wss.external.nodelay = true - -## The compress flag for external WebSocket/SSL connections. -## -## If this Value is set true,the websocket message would be compressed -## -## Value: true | false -## listener.wss.external.compress = true - -## The level of deflate options for external WebSocket/SSL connections. -## -## See: listener.wss.$name.deflate_opts.level -## -## Value: none | default | best_compression | best_speed -## listener.wss.external.deflate_opts.level = default - -## The mem_level of deflate options for external WebSocket/SSL connections. -## -## See: listener.wss.$name.deflate_opts.mem_level -## -## Valid range is 1-9 -## listener.wss.external.deflate_opts.mem_level = 8 - -## The strategy of deflate options for external WebSocket/SSL connections. -## -## See: listener.wss.$name.deflate_opts.strategy -## -## Value: default | filtered | huffman_only | rle -## listener.wss.external.deflate_opts.strategy = default - -## The deflate option for external WebSocket/SSL connections. -## -## See: listener.wss.$name.deflate_opts.server_context_takeover -## -## Value: takeover | no_takeover -## listener.wss.external.deflate_opts.server_context_takeover = takeover - -## The deflate option for external WebSocket/SSL connections. -## -## See: listener.wss.$name.deflate_opts.client_context_takeover -## -## Value: takeover | no_takeover -## listener.wss.external.deflate_opts.client_context_takeover = takeover - -## The deflate options for external WebSocket/SSL connections. -## -## See: listener.wss.$name.deflate_opts.server_max_window_bits -## -## Valid range is 8-15 -## listener.wss.external.deflate_opts.server_max_window_bits = 15 - -## The deflate options for external WebSocket/SSL connections. -## -## See: listener.wss.$name.deflate_opts.client_max_window_bits -## -## Valid range is 8-15 -## listener.wss.external.deflate_opts.client_max_window_bits = 15 - -## The idle timeout for external WebSocket/SSL connections. -## -## See: listener.wss.$name.idle_timeout -## -## Value: Duration -## listener.wss.external.idle_timeout = 60s - -## The max frame size for external WebSocket/SSL connections. -## -## Value: Number -## listener.wss.external.max_frame_size = 0 - -## Whether a WebSocket message is allowed to contain multiple MQTT packets -## -## Value: single | multiple -listener.wss.external.mqtt_piggyback = multiple - -##-------------------------------------------------------------------- -## Modules -##-------------------------------------------------------------------- -## The file to store loaded module names. -## -## Value: File -modules.loaded_file = {{ platform_data_dir }}/loaded_modules - -##-------------------------------------------------------------------- -## Presence Module - -## Sets the QoS for presence MQTT message. -## -## Value: 0 | 1 | 2 -module.presence.qos = 1 - -##-------------------------------------------------------------------- -## Subscription Module - -## Subscribe the Topics automatically when client connected. -## -## Value: String -## module.subscription.1.topic = connected/%c/%u - -## Qos of the proxy subscription. -## -## Value: 0 | 1 | 2 -## Default: 0 -## module.subscription.1.qos = 0 - -## No Local of the proxy subscription options. -## This configuration only takes effect in the MQTT V5 protocol. -## -## Value: 0 | 1 -## Default: 0 -## module.subscription.1.nl = 0 - -## Retain As Published of the proxy subscription options. -## This configuration only takes effect in the MQTT V5 protocol. -## -## Value: 0 | 1 -## Default: 0 -## module.subscription.1.rap = 0 - -## Retain Handling of the proxy subscription options. -## This configuration only takes effect in the MQTT V5 protocol. -## -## Value: 0 | 1 | 2 -## Default: 0 -## module.subscription.1.rh = 0 - -##-------------------------------------------------------------------- -## Rewrite Module - -## {rewrite, Topic, Re, Dest} -## module.rewrite.pub.rule.1 = x/# ^x/y/(.+)$ z/y/$1 -## module.rewrite.sub.rule.1 = y/+/z/# ^y/(.+)/z/(.+)$ y/z/$2 - -##------------------------------------------------------------------- -## Plugins -##------------------------------------------------------------------- - -## The etc dir for plugins' config. -## -## Value: Folder -plugins.etc_dir = {{ platform_etc_dir }}/plugins/ - -## The file to store loaded plugin names. -## -## Value: File -plugins.loaded_file = {{ platform_data_dir }}/loaded_plugins - -## The directory of extension plugins. -## -## Value: File -plugins.expand_plugins_dir = {{ platform_plugins_dir }}/ - ##-------------------------------------------------------------------- ## Broker ##-------------------------------------------------------------------- @@ -2069,153 +296,18 @@ broker.shared_dispatch_ack_enabled = false ## Value: Flag broker.route_batch_clean = off -##-------------------------------------------------------------------- -## System Monitor -##-------------------------------------------------------------------- +##------------------------------------------------------------------- +## Plugins +##------------------------------------------------------------------- -## Enable Long GC monitoring. Disable if the value is 0. -## Notice: don't enable the monitor in production for: -## https://github.com/erlang/otp/blob/feb45017da36be78d4c5784d758ede619fa7bfd3/erts/emulator/beam/erl_gc.c#L421 +## The etc dir for plugins' config. ## -## Value: Duration -## - h: hour -## - m: minute -## - s: second -## - ms: milliseconds -## -## Examples: -## - 2h: 2 hours -## - 30m: 30 minutes -## - 0.1s: 0.1 seconds -## - 100ms : 100 milliseconds -## -## Default: 0ms -sysmon.long_gc = 0 +## Value: Folder +plugins.etc_dir = {{ platform_etc_dir }}/plugins/ -## Enable Long Schedule(ms) monitoring. +## The file to store loaded plugin names. ## -## See: http://erlang.org/doc/man/erlang.html#system_monitor-2 -## -## Value: Duration -## - h: hour -## - m: minute -## - s: second -## - ms: milliseconds -## -## Examples: -## - 2h: 2 hours -## - 30m: 30 minutes -## - 0.1s: 0.1 seconds -## - 100ms: 100 milliseconds -## -## Default: 0ms -sysmon.long_schedule = 240ms - -## Enable Large Heap monitoring. -## -## See: http://erlang.org/doc/man/erlang.html#system_monitor-2 -## -## Value: bytes -## -## Default: 8M words. 32MB on 32-bit VM, 64MB on 64-bit VM. -sysmon.large_heap = 8MB - -## Enable Busy Port monitoring. -## -## See: http://erlang.org/doc/man/erlang.html#system_monitor-2 -## -## Value: true | false -sysmon.busy_port = false - -## Enable Busy Dist Port monitoring. -## -## See: http://erlang.org/doc/man/erlang.html#system_monitor-2 -## -## Value: true | false -sysmon.busy_dist_port = true - -## The time interval for the periodic cpu check -## -## Value: Duration -## -h: hour, e.g. '2h' for 2 hours -## -m: minute, e.g. '5m' for 5 minutes -## -s: second, e.g. '30s' for 30 seconds -## -## Default: 60s -os_mon.cpu_check_interval = 60s - -## The threshold, as percentage of system cpu, for how much system cpu can be used before the corresponding alarm is set. -## -## Default: 80% -os_mon.cpu_high_watermark = 80% - -## The threshold, as percentage of system cpu, for how much system cpu can be used before the corresponding alarm is clear. -## -## Default: 60% -os_mon.cpu_low_watermark = 60% - -## The time interval for the periodic memory check -## -## Value: Duration -## -h: hour, e.g. '2h' for 2 hours -## -m: minute, e.g. '5m' for 5 minutes -## -s: second, e.g. '30s' for 30 seconds -## -## Default: 60s -os_mon.mem_check_interval = 60s - -## The threshold, as percentage of system memory, for how much system memory can be allocated before the corresponding alarm is set. -## -## Default: 70% -os_mon.sysmem_high_watermark = 70% - -## The threshold, as percentage of system memory, for how much system memory can be allocated by one Erlang process before the corresponding alarm is set. -## -## Default: 5% -os_mon.procmem_high_watermark = 5% - -## The time interval for the periodic process limit check -## -## Value: Duration -## -## Default: 30s -vm_mon.check_interval = 30s - -## The threshold, as percentage of processes, for how many processes can simultaneously exist at the local node before the corresponding alarm is set. -## -## Default: 80% -vm_mon.process_high_watermark = 80% - -## The threshold, as percentage of processes, for how many processes can simultaneously exist at the local node before the corresponding alarm is clear. -## -## Default: 60% -vm_mon.process_low_watermark = 60% - -## Specifies the actions to take when an alarm is activated -## -## Value: String -## - log -## - publish -## -## Default: log,publish -alarm.actions = log,publish - -## The maximum number of deactivated alarms -## -## Value: Integer -## -## Default: 1000 -alarm.size_limit = 1000 - -## Validity Period of deactivated alarms -## -## Value: Duration -## - h: hour -## - m: minute -## - s: second -## - ms: milliseconds -## -## Default: 24h -alarm.validity_period = 24h +## Value: File +plugins.loaded_file = {{ platform_data_dir }}/loaded_plugins {{ additional_configs }} diff --git a/etc/listeners.conf b/etc/listeners.conf new file mode 100644 index 000000000..2f2f1b5a1 --- /dev/null +++ b/etc/listeners.conf @@ -0,0 +1,928 @@ +##-------------------------------------------------------------------- +## Listeners +##-------------------------------------------------------------------- + +##-------------------------------------------------------------------- +## MQTT/TCP - External TCP Listener for MQTT Protocol + +## listener.tcp.$name is the IP address and port that the MQTT/TCP +## listener will bind. +## +## Value: IP:Port | Port +## +## Examples: 1883, 127.0.0.1:1883, ::1:1883 +listener.tcp.external = 0.0.0.0:1883 + +## The acceptor pool for external MQTT/TCP listener. +## +## Value: Number +listener.tcp.external.acceptors = 8 + +## Maximum number of concurrent MQTT/TCP connections. +## +## Value: Number +listener.tcp.external.max_connections = 1024000 + +## Maximum external connections per second. +## +## Value: Number +listener.tcp.external.max_conn_rate = 1000 + +## Specify the {active, N} option for the external MQTT/TCP Socket. +## +## Value: Number +listener.tcp.external.active_n = 100 + +## Zone of the external MQTT/TCP listener belonged to. +## +## See: zone.$name.* +## +## Value: String +listener.tcp.external.zone = external + +## The access control rules for the MQTT/TCP listener. +## +## See: https://github.com/emqtt/esockd#allowdeny +## +## Value: ACL Rule +## +## Example: allow 192.168.0.0/24 +listener.tcp.external.access.1 = allow all + +## Enable the Proxy Protocol V1/2 if the EMQ X cluster is deployed +## behind HAProxy or Nginx. +## +## See: https://www.haproxy.com/blog/haproxy/proxy-protocol/ +## +## Value: on | off +## listener.tcp.external.proxy_protocol = on + +## Sets the timeout for proxy protocol. EMQ X will close the TCP connection +## if no proxy protocol packet recevied within the timeout. +## +## Value: Duration +## listener.tcp.external.proxy_protocol_timeout = 3s + +## Enable the option for X.509 certificate based authentication. +## EMQX will use the common name of certificate as MQTT username. +## +## Value: cn | dn | crt +## listener.tcp.external.peer_cert_as_username = cn + +## The TCP backlog defines the maximum length that the queue of pending +## connections can grow to. +## +## Value: Number >= 0 +listener.tcp.external.backlog = 1024 + +## The TCP send timeout for external MQTT connections. +## +## Value: Duration +listener.tcp.external.send_timeout = 15s + +## Close the TCP connection if send timeout. +## +## Value: on | off +listener.tcp.external.send_timeout_close = on + +## The TCP receive buffer(os kernel) for MQTT connections. +## +## See: http://erlang.org/doc/man/inet.html +## +## Value: Bytes +## listener.tcp.external.recbuf = 2KB + +## The TCP send buffer(os kernel) for MQTT connections. +## +## See: http://erlang.org/doc/man/inet.html +## +## Value: Bytes +## listener.tcp.external.sndbuf = 2KB + +## The size of the user-level software buffer used by the driver. +## Not to be confused with options sndbuf and recbuf, which correspond +## to the Kernel socket buffers. It is recommended to have val(buffer) +## >= max(val(sndbuf),val(recbuf)) to avoid performance issues because +## of unnecessary copying. val(buffer) is automatically set to the above +## maximum when values sndbuf or recbuf are set. +## +## See: http://erlang.org/doc/man/inet.html +## +## Value: Bytes +## listener.tcp.external.buffer = 2KB + +## Sets the 'buffer = max(sndbuf, recbuf)' if this option is enabled. +## +## Value: on | off +## listener.tcp.external.tune_buffer = off + +## The TCP_NODELAY flag for MQTT connections. Small amounts of data are +## sent immediately if the option is enabled. +## +## Value: true | false +listener.tcp.external.nodelay = true + +## The SO_REUSEADDR flag for TCP listener. +## +## Value: true | false +listener.tcp.external.reuseaddr = true + +##-------------------------------------------------------------------- +## Internal TCP Listener for MQTT Protocol + +## The IP address and port that the internal MQTT/TCP protocol listener +## will bind. +## +## Value: IP:Port, Port +## +## Examples: 11883, 127.0.0.1:11883, ::1:11883 +listener.tcp.internal = 127.0.0.1:11883 + +## The acceptor pool for internal MQTT/TCP listener. +## +## Value: Number +listener.tcp.internal.acceptors = 4 + +## Maximum number of concurrent MQTT/TCP connections. +## +## Value: Number +listener.tcp.internal.max_connections = 1024000 + +## Maximum internal connections per second. +## +## Value: Number +listener.tcp.internal.max_conn_rate = 1000 + +## Specify the {active, N} option for the internal MQTT/TCP Socket. +## +## Value: Number +listener.tcp.internal.active_n = 1000 + +## Zone of the internal MQTT/TCP listener belonged to. +## +## Value: String +listener.tcp.internal.zone = internal + +## The TCP backlog of internal MQTT/TCP Listener. +## +## See: listener.tcp.$name.backlog +## +## Value: Number >= 0 +listener.tcp.internal.backlog = 512 + +## The TCP send timeout for internal MQTT connections. +## +## See: listener.tcp.$name.send_timeout +## +## Value: Duration +listener.tcp.internal.send_timeout = 5s + +## Close the MQTT/TCP connection if send timeout. +## +## See: listener.tcp.$name.send_timeout_close +## +## Value: on | off +listener.tcp.internal.send_timeout_close = on + +## The TCP receive buffer(os kernel) for internal MQTT connections. +## +## See: listener.tcp.$name.recbuf +## +## Value: Bytes +listener.tcp.internal.recbuf = 64KB + +## The TCP send buffer(os kernel) for internal MQTT connections. +## +## See: http://erlang.org/doc/man/inet.html +## +## Value: Bytes +listener.tcp.internal.sndbuf = 64KB + +## The size of the user-level software buffer used by the driver. +## +## See: listener.tcp.$name.buffer +## +## Value: Bytes +## listener.tcp.internal.buffer = 16KB + +## Sets the 'buffer = max(sndbuf, recbuf)' if this option is enabled. +## +## See: listener.tcp.$name.tune_buffer +## +## Value: on | off +## listener.tcp.internal.tune_buffer = off + +## The TCP_NODELAY flag for internal MQTT connections. +## +## See: listener.tcp.$name.nodelay +## +## Value: true | false +listener.tcp.internal.nodelay = false + +## The SO_REUSEADDR flag for MQTT/TCP Listener. +## +## Value: true | false +listener.tcp.internal.reuseaddr = true + +##-------------------------------------------------------------------- +## MQTT/SSL - External SSL Listener for MQTT Protocol + +## listener.ssl.$name is the IP address and port that the MQTT/SSL +## listener will bind. +## +## Value: IP:Port | Port +## +## Examples: 8883, 127.0.0.1:8883, ::1:8883 +listener.ssl.external = 8883 + +## The acceptor pool for external MQTT/SSL listener. +## +## Value: Number +listener.ssl.external.acceptors = 16 + +## Maximum number of concurrent MQTT/SSL connections. +## +## Value: Number +listener.ssl.external.max_connections = 102400 + +## Maximum MQTT/SSL connections per second. +## +## Value: Number +listener.ssl.external.max_conn_rate = 500 + +## Specify the {active, N} option for the internal MQTT/SSL Socket. +## +## Value: Number +listener.ssl.external.active_n = 100 + +## Zone of the external MQTT/SSL listener belonged to. +## +## Value: String +listener.ssl.external.zone = external + +## The access control rules for the MQTT/SSL listener. +## +## See: listener.tcp.$name.access +## +## Value: ACL Rule +listener.ssl.external.access.1 = allow all + +## Enable the Proxy Protocol V1/2 if the EMQ cluster is deployed behind +## HAProxy or Nginx. +## +## See: listener.tcp.$name.proxy_protocol +## +## Value: on | off +## listener.ssl.external.proxy_protocol = on + +## Sets the timeout for proxy protocol. +## +## See: listener.tcp.$name.proxy_protocol_timeout +## +## Value: Duration +## listener.ssl.external.proxy_protocol_timeout = 3s + +## TLS versions only to protect from POODLE attack. +## +## See: http://erlang.org/doc/man/ssl.html +## +## Value: String, seperated by ',' +## listener.ssl.external.tls_versions = tlsv1.2,tlsv1.1,tlsv1 + +## TLS Handshake timeout. +## +## Value: Duration +listener.ssl.external.handshake_timeout = 15s + +## Path to the file containing the user's private PEM-encoded key. +## +## See: http://erlang.org/doc/man/ssl.html +## +## Value: File +listener.ssl.external.keyfile = {{ platform_etc_dir }}/certs/key.pem + +## Path to a file containing the user certificate. +## +## See: http://erlang.org/doc/man/ssl.html +## +## Value: File +listener.ssl.external.certfile = {{ platform_etc_dir }}/certs/cert.pem + +## Path to the file containing PEM-encoded CA certificates. The CA certificates +## are used during server authentication and when building the client certificate chain. +## +## Value: File +## listener.ssl.external.cacertfile = {{ platform_etc_dir }}/certs/cacert.pem + +## The Ephemeral Diffie-Helman key exchange is a very effective way of +## ensuring Forward Secrecy by exchanging a set of keys that never hit +## the wire. Since the DH key is effectively signed by the private key, +## it needs to be at least as strong as the private key. In addition, +## the default DH groups that most of the OpenSSL installations have +## are only a handful (since they are distributed with the OpenSSL +## package that has been built for the operating system it’s running on) +## and hence predictable (not to mention, 1024 bits only). +## In order to escape this situation, first we need to generate a fresh, +## strong DH group, store it in a file and then use the option above, +## to force our SSL application to use the new DH group. Fortunately, +## OpenSSL provides us with a tool to do that. Simply run: +## openssl dhparam -out dh-params.pem 2048 +## +## Value: File +## listener.ssl.external.dhfile = {{ platform_etc_dir }}/certs/dh-params.pem + +## A server only does x509-path validation in mode verify_peer, +## as it then sends a certificate request to the client (this +## message is not sent if the verify option is verify_none). +## You can then also want to specify option fail_if_no_peer_cert. +## More information at: http://erlang.org/doc/man/ssl.html +## +## Value: verify_peer | verify_none +## listener.ssl.external.verify = verify_peer + +## Used together with {verify, verify_peer} by an SSL server. If set to true, +## the server fails if the client does not have a certificate to send, that is, +## sends an empty certificate. +## +## Value: true | false +## listener.ssl.external.fail_if_no_peer_cert = true + +## This is the single most important configuration option of an Erlang SSL +## application. Ciphers (and their ordering) define the way the client and +## server encrypt information over the wire, from the initial Diffie-Helman +## key exchange, the session key encryption ## algorithm and the message +## digest algorithm. Selecting a good cipher suite is critical for the +## application’s data security, confidentiality and performance. +## +## The cipher list above offers: +## +## A good balance between compatibility with older browsers. +## It can get stricter for Machine-To-Machine scenarios. +## Perfect Forward Secrecy. +## No old/insecure encryption and HMAC algorithms +## +## Most of it was copied from Mozilla’s Server Side TLS article +## +## Value: Ciphers +listener.ssl.external.ciphers = ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES256-SHA384,ECDHE-RSA-AES256-SHA384,ECDHE-ECDSA-DES-CBC3-SHA,ECDH-ECDSA-AES256-GCM-SHA384,ECDH-RSA-AES256-GCM-SHA384,ECDH-ECDSA-AES256-SHA384,ECDH-RSA-AES256-SHA384,DHE-DSS-AES256-GCM-SHA384,DHE-DSS-AES256-SHA256,AES256-GCM-SHA384,AES256-SHA256,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES128-SHA256,ECDHE-RSA-AES128-SHA256,ECDH-ECDSA-AES128-GCM-SHA256,ECDH-RSA-AES128-GCM-SHA256,ECDH-ECDSA-AES128-SHA256,ECDH-RSA-AES128-SHA256,DHE-DSS-AES128-GCM-SHA256,DHE-DSS-AES128-SHA256,AES128-GCM-SHA256,AES128-SHA256,ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,DHE-DSS-AES256-SHA,ECDH-ECDSA-AES256-SHA,ECDH-RSA-AES256-SHA,AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,DHE-DSS-AES128-SHA,ECDH-ECDSA-AES128-SHA,ECDH-RSA-AES128-SHA,AES128-SHA + +## Ciphers for TLS PSK. +## Note that 'listener.ssl.external.ciphers' and 'listener.ssl.external.psk_ciphers' cannot +## be configured at the same time. +## See 'https://tools.ietf.org/html/rfc4279#section-2'. +#listener.ssl.external.psk_ciphers = PSK-AES128-CBC-SHA,PSK-AES256-CBC-SHA,PSK-3DES-EDE-CBC-SHA,PSK-RC4-SHA + +## SSL parameter renegotiation is a feature that allows a client and a server +## to renegotiate the parameters of the SSL connection on the fly. +## RFC 5746 defines a more secure way of doing this. By enabling secure renegotiation, +## you drop support for the insecure renegotiation, prone to MitM attacks. +## +## Value: on | off +## listener.ssl.external.secure_renegotiate = off + +## A performance optimization setting, it allows clients to reuse +## pre-existing sessions, instead of initializing new ones. +## Read more about it here. +## +## See: http://erlang.org/doc/man/ssl.html +## +## Value: on | off +## listener.ssl.external.reuse_sessions = on + +## An important security setting, it forces the cipher to be set based +## on the server-specified order instead of the client-specified order, +## hence enforcing the (usually more properly configured) security +## ordering of the server administrator. +## +## Value: on | off +## listener.ssl.external.honor_cipher_order = on + +## Use the CN, DN or CRT field from the client certificate as a username. +## Notice that 'verify' should be set as 'verify_peer'. +## +## Value: cn | dn | crt +## listener.ssl.external.peer_cert_as_username = cn + +## TCP backlog for the SSL connection. +## +## See listener.tcp.$name.backlog +## +## Value: Number >= 0 +## listener.ssl.external.backlog = 1024 + +## The TCP send timeout for the SSL connection. +## +## See listener.tcp.$name.send_timeout +## +## Value: Duration +## listener.ssl.external.send_timeout = 15s + +## Close the SSL connection if send timeout. +## +## See: listener.tcp.$name.send_timeout_close +## +## Value: on | off +## listener.ssl.external.send_timeout_close = on + +## The TCP receive buffer(os kernel) for the SSL connections. +## +## See: listener.tcp.$name.recbuf +## +## Value: Bytes +## listener.ssl.external.recbuf = 4KB + +## The TCP send buffer(os kernel) for internal MQTT connections. +## +## See: listener.tcp.$name.sndbuf +## +## Value: Bytes +## listener.ssl.external.sndbuf = 4KB + +## The size of the user-level software buffer used by the driver. +## +## See: listener.tcp.$name.buffer +## +## Value: Bytes +## listener.ssl.external.buffer = 4KB + +## Sets the 'buffer = max(sndbuf, recbuf)' if this option is enabled. +## +## See: listener.tcp.$name.tune_buffer +## +## Value: on | off +## listener.ssl.external.tune_buffer = off + +## The TCP_NODELAY flag for SSL connections. +## +## See: listener.tcp.$name.nodelay +## +## Value: true | false +## listener.ssl.external.nodelay = true + +## The SO_REUSEADDR flag for MQTT/SSL Listener. +## +## Value: true | false +listener.ssl.external.reuseaddr = true + +##-------------------------------------------------------------------- +## External WebSocket listener for MQTT protocol + +## listener.ws.$name is the IP address and port that the MQTT/WebSocket +## listener will bind. +## +## Value: IP:Port | Port +## +## Examples: 8083, 127.0.0.1:8083, ::1:8083 +listener.ws.external = 8083 + +## The path of WebSocket MQTT endpoint +## +## Value: URL Path +listener.ws.external.mqtt_path = /mqtt + +## The acceptor pool for external MQTT/WebSocket listener. +## +## Value: Number +listener.ws.external.acceptors = 4 + +## Maximum number of concurrent MQTT/WebSocket connections. +## +## Value: Number +listener.ws.external.max_connections = 102400 + +## Maximum MQTT/WebSocket connections per second. +## +## Value: Number +listener.ws.external.max_conn_rate = 1000 + +## Simulate the {active, N} option for the MQTT/WebSocket connections. +## +## Value: Number +listener.ws.external.active_n = 100 + +## Zone of the external MQTT/WebSocket listener belonged to. +## +## Value: String +listener.ws.external.zone = external + +## The access control for the MQTT/WebSocket listener. +## +## See: listener.ws.$name.access +## +## Value: ACL Rule +listener.ws.external.access.1 = allow all + +## Verify if the protocol header is valid. Turn off for WeChat MiniApp. +## +## Value: on | off +listener.ws.external.verify_protocol_header = on + +## Enable the Proxy Protocol V1/2 if the EMQ cluster is deployed behind +## HAProxy or Nginx. +## +## See: listener.ws.$name.proxy_protocol +## +## Value: on | off +## listener.ws.external.proxy_protocol = on + +## Sets the timeout for proxy protocol. +## +## See: listener.ws.$name.proxy_protocol_timeout +## +## Value: Duration +## listener.ws.external.proxy_protocol_timeout = 3s + +## The TCP backlog of external MQTT/WebSocket Listener. +## +## See: listener.ws.$name.backlog +## +## Value: Number >= 0 +listener.ws.external.backlog = 1024 + +## The TCP send timeout for external MQTT/WebSocket connections. +## +## See: listener.ws.$name.send_timeout +## +## Value: Duration +listener.ws.external.send_timeout = 15s + +## Close the MQTT/WebSocket connection if send timeout. +## +## See: listener.ws.$name.send_timeout_close +## +## Value: on | off +listener.ws.external.send_timeout_close = on + +## The TCP receive buffer(os kernel) for external MQTT/WebSocket connections. +## +## See: listener.ws.$name.recbuf +## +## Value: Bytes +## listener.ws.external.recbuf = 2KB + +## The TCP send buffer(os kernel) for external MQTT/WebSocket connections. +## +## See: listener.ws.$name.sndbuf +## +## Value: Bytes +## listener.ws.external.sndbuf = 2KB + +## The size of the user-level software buffer used by the driver. +## +## See: listener.ws.$name.buffer +## +## Value: Bytes +## listener.ws.external.buffer = 2KB + +## Sets the 'buffer = max(sndbuf, recbuf)' if this option is enabled. +## +## See: listener.ws.$name.tune_buffer +## +## Value: on | off +## listener.ws.external.tune_buffer = off + +## The TCP_NODELAY flag for external MQTT/WebSocket connections. +## +## See: listener.ws.$name.nodelay +## +## Value: true | false +listener.ws.external.nodelay = true + +## The compress flag for external MQTT/WebSocket connections. +## +## If this Value is set true,the websocket message would be compressed +## +## Value: true | false +## listener.ws.external.compress = true + +## The level of deflate options for external MQTT/WebSocket connections. +## +## See: listener.ws.$name.deflate_opts.level +## +## Value: none | default | best_compression | best_speed +## listener.ws.external.deflate_opts.level = default + +## The mem_level of deflate options for external MQTT/WebSocket connections. +## +## See: listener.ws.$name.deflate_opts.mem_level +## +## Valid range is 1-9 +## listener.ws.external.deflate_opts.mem_level = 8 + +## The strategy of deflate options for external MQTT/WebSocket connections. +## +## See: listener.ws.$name.deflate_opts.strategy +## +## Value: default | filtered | huffman_only | rle +## listener.ws.external.deflate_opts.strategy = default + +## The deflate option for external MQTT/WebSocket connections. +## +## See: listener.ws.$name.deflate_opts.server_context_takeover +## +## Value: takeover | no_takeover +## listener.ws.external.deflate_opts.server_context_takeover = takeover + +## The deflate option for external MQTT/WebSocket connections. +## +## See: listener.ws.$name.deflate_opts.client_context_takeover +## +## Value: takeover | no_takeover +## listener.ws.external.deflate_opts.client_context_takeover = takeover + +## The deflate options for external MQTT/WebSocket connections. +## +## See: listener.ws.$name.deflate_opts.server_max_window_bits +## +## Valid range is 8-15 +## listener.ws.external.deflate_opts.server_max_window_bits = 15 + +## The deflate options for external MQTT/WebSocket connections. +## +## See: listener.ws.$name.deflate_opts.client_max_window_bits +## +## Valid range is 8-15 +## listener.ws.external.deflate_opts.client_max_window_bits = 15 + +## The idle timeout for external MQTT/WebSocket connections. +## +## See: listener.ws.$name.idle_timeout +## +## Value: Duration +## listener.ws.external.idle_timeout = 60s + +## The max frame size for external MQTT/WebSocket connections. +## +## +## Value: Number +## listener.ws.external.max_frame_size = 0 + +## Whether a WebSocket message is allowed to contain multiple MQTT packets +## +## Value: single | multiple +listener.ws.external.mqtt_piggyback = multiple + +##-------------------------------------------------------------------- +## External WebSocket/SSL listener for MQTT Protocol + +## listener.wss.$name is the IP address and port that the MQTT/WebSocket/SSL +## listener will bind. +## +## Value: IP:Port | Port +## +## Examples: 8084, 127.0.0.1:8084, ::1:8084 +listener.wss.external = 8084 + +## The path of WebSocket MQTT endpoint +## +## Value: URL Path +listener.wss.external.mqtt_path = /mqtt + +## The acceptor pool for external MQTT/WebSocket/SSL listener. +## +## Value: Number +listener.wss.external.acceptors = 4 + +## Maximum number of concurrent MQTT/Webwocket/SSL connections. +## +## Value: Number +listener.wss.external.max_connections = 16 + +## Maximum MQTT/WebSocket/SSL connections per second. +## +## See: listener.tcp.$name.max_conn_rate +## +## Value: Number +listener.wss.external.max_conn_rate = 1000 + +## Simulate the {active, N} option for the MQTT/WebSocket/SSL connections. +## +## Value: Number +listener.wss.external.active_n = 100 + +## Zone of the external MQTT/WebSocket/SSL listener belonged to. +## +## Value: String +listener.wss.external.zone = external + +## The access control rules for the MQTT/WebSocket/SSL listener. +## +## See: listener.tcp.$name.access. +## +## Value: ACL Rule +listener.wss.external.access.1 = allow all + +## See: listener.ws.external.verify_protocol_header +## +## Value: on | off +listener.wss.external.verify_protocol_header = on + +## Enable the Proxy Protocol V1/2 support. +## +## See: listener.tcp.$name.proxy_protocol +## +## Value: on | off +## listener.wss.external.proxy_protocol = on + +## Sets the timeout for proxy protocol. +## +## See: listener.tcp.$name.proxy_protocol_timeout +## +## Value: Duration +## listener.wss.external.proxy_protocol_timeout = 3s + +## TLS versions only to protect from POODLE attack. +## +## See: listener.ssl.$name.tls_versions +## +## Value: String, seperated by ',' +## listener.wss.external.tls_versions = tlsv1.2,tlsv1.1,tlsv1 + +## Path to the file containing the user's private PEM-encoded key. +## +## See: listener.ssl.$name.keyfile +## +## Value: File +listener.wss.external.keyfile = {{ platform_etc_dir }}/certs/key.pem + +## Path to a file containing the user certificate. +## +## See: listener.ssl.$name.certfile +## +## Value: File +listener.wss.external.certfile = {{ platform_etc_dir }}/certs/cert.pem + +## Path to the file containing PEM-encoded CA certificates. +## +## See: listener.ssl.$name.cacert +## +## Value: File +## listener.wss.external.cacertfile = {{ platform_etc_dir }}/certs/cacert.pem + +## See: listener.ssl.$name.dhfile +## +## Value: File +## listener.ssl.external.dhfile = {{ platform_etc_dir }}/certs/dh-params.pem + +## See: listener.ssl.$name.vefify +## +## Value: vefify_peer | verify_none +## listener.wss.external.verify = verify_peer + +## See: listener.ssl.$name.fail_if_no_peer_cert +## +## Value: false | true +## listener.wss.external.fail_if_no_peer_cert = true + +## See: listener.ssl.$name.ciphers +## +## Value: Ciphers +listener.wss.external.ciphers = ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES256-SHA384,ECDHE-RSA-AES256-SHA384,ECDHE-ECDSA-DES-CBC3-SHA,ECDH-ECDSA-AES256-GCM-SHA384,ECDH-RSA-AES256-GCM-SHA384,ECDH-ECDSA-AES256-SHA384,ECDH-RSA-AES256-SHA384,DHE-DSS-AES256-GCM-SHA384,DHE-DSS-AES256-SHA256,AES256-GCM-SHA384,AES256-SHA256,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES128-SHA256,ECDHE-RSA-AES128-SHA256,ECDH-ECDSA-AES128-GCM-SHA256,ECDH-RSA-AES128-GCM-SHA256,ECDH-ECDSA-AES128-SHA256,ECDH-RSA-AES128-SHA256,DHE-DSS-AES128-GCM-SHA256,DHE-DSS-AES128-SHA256,AES128-GCM-SHA256,AES128-SHA256,ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,DHE-DSS-AES256-SHA,ECDH-ECDSA-AES256-SHA,ECDH-RSA-AES256-SHA,AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,DHE-DSS-AES128-SHA,ECDH-ECDSA-AES128-SHA,ECDH-RSA-AES128-SHA,AES128-SHA + +## Ciphers for TLS PSK. +## Note that 'listener.wss.external.ciphers' and 'listener.wss.external.psk_ciphers' cannot +## be configured at the same time. +## See 'https://tools.ietf.org/html/rfc4279#section-2'. +## listener.wss.external.psk_ciphers = PSK-AES128-CBC-SHA,PSK-AES256-CBC-SHA,PSK-3DES-EDE-CBC-SHA,PSK-RC4-SHA + +## See: listener.ssl.$name.secure_renegotiate +## +## Value: on | off +## listener.wss.external.secure_renegotiate = off + +## See: listener.ssl.$name.reuse_sessions +## +## Value: on | off +## listener.wss.external.reuse_sessions = on + +## See: listener.ssl.$name.honor_cipher_order +## +## Value: on | off +## listener.wss.external.honor_cipher_order = on + +## See: listener.ssl.$name.peer_cert_as_username +## +## Value: cn | dn | crt +## listener.wss.external.peer_cert_as_username = cn + +## TCP backlog for the WebSocket/SSL connection. +## +## See: listener.tcp.$name.backlog +## +## Value: Number >= 0 +listener.wss.external.backlog = 1024 + +## The TCP send timeout for the WebSocket/SSL connection. +## +## See: listener.tcp.$name.send_timeout +## +## Value: Duration +listener.wss.external.send_timeout = 15s + +## Close the WebSocket/SSL connection if send timeout. +## +## See: listener.tcp.$name.send_timeout_close +## +## Value: on | off +listener.wss.external.send_timeout_close = on + +## The TCP receive buffer(os kernel) for the WebSocket/SSL connections. +## +## See: listener.tcp.$name.recbuf +## +## Value: Bytes +## listener.wss.external.recbuf = 4KB + +## The TCP send buffer(os kernel) for the WebSocket/SSL connections. +## +## See: listener.tcp.$name.sndbuf +## +## Value: Bytes +## listener.wss.external.sndbuf = 4KB + +## The size of the user-level software buffer used by the driver. +## +## See: listener.tcp.$name.buffer +## +## Value: Bytes +## listener.wss.external.buffer = 4KB + +## The TCP_NODELAY flag for WebSocket/SSL connections. +## +## See: listener.tcp.$name.nodelay +## +## Value: true | false +## listener.wss.external.nodelay = true + +## The compress flag for external WebSocket/SSL connections. +## +## If this Value is set true,the websocket message would be compressed +## +## Value: true | false +## listener.wss.external.compress = true + +## The level of deflate options for external WebSocket/SSL connections. +## +## See: listener.wss.$name.deflate_opts.level +## +## Value: none | default | best_compression | best_speed +## listener.wss.external.deflate_opts.level = default + +## The mem_level of deflate options for external WebSocket/SSL connections. +## +## See: listener.wss.$name.deflate_opts.mem_level +## +## Valid range is 1-9 +## listener.wss.external.deflate_opts.mem_level = 8 + +## The strategy of deflate options for external WebSocket/SSL connections. +## +## See: listener.wss.$name.deflate_opts.strategy +## +## Value: default | filtered | huffman_only | rle +## listener.wss.external.deflate_opts.strategy = default + +## The deflate option for external WebSocket/SSL connections. +## +## See: listener.wss.$name.deflate_opts.server_context_takeover +## +## Value: takeover | no_takeover +## listener.wss.external.deflate_opts.server_context_takeover = takeover + +## The deflate option for external WebSocket/SSL connections. +## +## See: listener.wss.$name.deflate_opts.client_context_takeover +## +## Value: takeover | no_takeover +## listener.wss.external.deflate_opts.client_context_takeover = takeover + +## The deflate options for external WebSocket/SSL connections. +## +## See: listener.wss.$name.deflate_opts.server_max_window_bits +## +## Valid range is 8-15 +## listener.wss.external.deflate_opts.server_max_window_bits = 15 + +## The deflate options for external WebSocket/SSL connections. +## +## See: listener.wss.$name.deflate_opts.client_max_window_bits +## +## Valid range is 8-15 +## listener.wss.external.deflate_opts.client_max_window_bits = 15 + +## The idle timeout for external WebSocket/SSL connections. +## +## See: listener.wss.$name.idle_timeout +## +## Value: Duration +## listener.wss.external.idle_timeout = 60s + +## The max frame size for external WebSocket/SSL connections. +## +## Value: Number +## listener.wss.external.max_frame_size = 0 + +## Whether a WebSocket message is allowed to contain multiple MQTT packets +## +## Value: single | multiple +listener.wss.external.mqtt_piggyback = multiple \ No newline at end of file diff --git a/etc/logger.conf b/etc/logger.conf new file mode 100644 index 000000000..4fd0faf5f --- /dev/null +++ b/etc/logger.conf @@ -0,0 +1,170 @@ +##-------------------------------------------------------------------- +## Log +##-------------------------------------------------------------------- + +## Where to emit the logs. +## Enable the console (standard output) logs. +## +## Value: off | file | console | both +## - off: disable logs entirely +## - file: write logs only to file +## - console: write logs only to standard I/O +## - both: write logs both to file and standard I/O +log.to = both + +## The log severity level. +## +## Value: debug | info | notice | warning | error | critical | alert | emergency +## +## Note: Only the messages with severity level higher than or equal to +## this level will be logged. +## +## Default: warning +log.level = warning + +## The dir for log files. +## +## Value: Folder +log.dir = {{ platform_log_dir }} + +## The log filename for logs of level specified in "log.level". +## +## If `log.rotation` is enabled, this is the base name of the +## files. Each file in a rotated log is named .N, where N is an integer. +## +## Value: String +## Default: emqx.log +log.file = emqx.log + +## Limits the total number of characters printed for each log event. +## +## Value: Integer +## Default: No Limit +#log.chars_limit = 8192 + +## Enables the log rotation. +## With this enabled, new log files will be created when the current +## log file is full, max to `log.rotation.size` files will be created. +## +## Value: on | off +## Default: on +log.rotation = on + +## Maximum size of each log file. +## +## Value: Number +## Default: 10M +## Supported Unit: KB | MB | GB +log.rotation.size = 10MB + +## Maximum rotation count of log files. +## +## Value: Number +## Default: 5 +log.rotation.count = 5 + +## To create additional log files for specific log levels. +## +## Value: File Name +## Format: log.$level.file = $filename, +## where "$level" can be one of: debug, info, notice, warning, +## error, critical, alert, emergency +## Note: Log files for a specific log level will only contain all the logs +## that higher than or equal to that level +## +#log.info.file = info.log +#log.error.file = error.log + +## The max allowed queue length before switching to sync mode. +## +## Log overload protection parameter. If the message queue grows +## larger than this value the handler switches from anync to sync mode. +## +## Default: 100 +## +#log.sync_mode_qlen = 100 + +## The max allowed queue length before switching to drop mode. +## +## Log overload protection parameter. When the message queue grows +## larger than this threshold, the handler switches to a mode in which +## it drops all new events that senders want to log. +## +## Default: 3000 +## +#log.drop_mode_qlen = 3000 + +## The max allowed queue length before switching to flush mode. +## +## Log overload protection parameter. If the length of the message queue +## grows larger than this threshold, a flush (delete) operation takes place. +## To flush events, the handler discards the messages in the message queue +## by receiving them in a loop without logging. +## +## Default: 8000 +## +#log.flush_qlen = 8000 + +## Kill the log handler when it gets overloaded. +## +## Log overload protection parameter. It is possible that a handler, +## even if it can successfully manage peaks of high load without crashing, +## can build up a large message queue, or use a large amount of memory. +## We could kill the log handler in these cases and restart it after a +## few seconds. +## +## Default: on +## +#log.overload_kill = on + +## The max allowed queue length before killing the log hanlder. +## +## Log overload protection parameter. This is the maximum allowed queue +## length. If the message queue grows larger than this, the handler +## process is terminated. +## +## Default: 20000 +## +#log.overload_kill_qlen = 20000 + +## The max allowed memory size before killing the log hanlder. +## +## Log overload protection parameter. This is the maximum memory size +## that the handler process is allowed to use. If the handler grows +## larger than this, the process is terminated. +## +## Default: 30MB +## +#log.overload_kill_mem_size = 30MB + +## Restart the log hanlder after some seconds. +## +## Log overload protection parameter. If the handler is terminated, +## it restarts automatically after a delay specified in seconds. +## The value "infinity" prevents restarts. +## +## Default: 5s +## +#log.overload_kill_restart_after = 5s + +## Max burst count and time window for burst control. +## +## Log overload protection parameter. Large bursts of log events - many +## events received by the handler under a short period of time - can +## potentially cause problems. By specifying the maximum number of events +## to be handled within a certain time frame, the handler can avoid +## choking the log with massive amounts of printouts. +## +## This config controls the maximum number of events to handle within +## a time frame. After the limit is reached, successive events are +## dropped until the end of the time frame. +## +## Note that there would be no warning if any messages were +## dropped because of burst control. +## +## Comment this config out to disable the burst control feature. +## +## Value: MaxBurstCount,TimeWindow +## Default: disabled +## +#log.burst_limit = 20000, 1s \ No newline at end of file diff --git a/etc/rpc.conf b/etc/rpc.conf new file mode 100644 index 000000000..d86838e4f --- /dev/null +++ b/etc/rpc.conf @@ -0,0 +1,98 @@ +##-------------------------------------------------------------------- +## RPC +##-------------------------------------------------------------------- +## RPC Mode. +## +## Value: sync | async +rpc.mode = async + +## Max batch size of async RPC requests. +## +## Value: Integer +## Zero or negative value disables rpc batching. +## +## NOTE: RPC batch won't work when rpc.mode = sync +rpc.async_batch_size = 256 + +## RPC port discovery +## +## The strategy for discovering the RPC listening port of other nodes. +## +## Value: Enum +## - manual: discover ports by `tcp_server_port` and `tcp_client_port`. +## - stateless: discover ports in a stateless manner. +## If node name is `emqx@127.0.0.1`, where the `` is an integer, +## then the listening port will be `5370 + ` +## +## Defaults to `stateless`. +rpc.port_discovery = stateless + +## TCP server port for RPC. +## +## Only takes effect when `rpc.port_discovery` = `manual`. +## +## Value: Port [1024-65535] +#rpc.tcp_server_port = 5369 + +## TCP port for outgoing RPC connections. +## +## Only takes effect when `rpc.port_discovery` = `manual`. +## +## Value: Port [1024-65535] +#rpc.tcp_client_port = 5369 + +## Number of outgoing RPC connections. +## +## Value: Interger [1-256] +## Defaults to NumberOfCPUSchedulers / 2 +#rpc.tcp_client_num = 1 + +## RCP Client connect timeout. +## +## Value: Seconds +rpc.connect_timeout = 5s + +## TCP send timeout of RPC client and server. +## +## Value: Seconds +rpc.send_timeout = 5s + +## Authentication timeout +## +## Value: Seconds +rpc.authentication_timeout = 5s + +## Default receive timeout for call() functions +## +## Value: Seconds +rpc.call_receive_timeout = 15s + +## Socket idle keepalive. +## +## Value: Seconds +rpc.socket_keepalive_idle = 900s + +## TCP Keepalive probes interval. +## +## Value: Seconds +rpc.socket_keepalive_interval = 75s + +## Probes lost to close the connection +## +## Value: Integer +rpc.socket_keepalive_count = 9 + +## Size of TCP send buffer. +## +## Value: Bytes +rpc.socket_sndbuf = 1MB + +## Size of TCP receive buffer. +## +## Value: Seconds +rpc.socket_recbuf = 1MB + +## Size of user-level software socket buffer. +## +## Value: Seconds +rpc.socket_buffer = 1MB diff --git a/etc/sys_mon.conf b/etc/sys_mon.conf new file mode 100644 index 000000000..abd8baa04 --- /dev/null +++ b/etc/sys_mon.conf @@ -0,0 +1,148 @@ +##-------------------------------------------------------------------- +## System Monitor +##-------------------------------------------------------------------- + +## Enable Long GC monitoring. Disable if the value is 0. +## Notice: don't enable the monitor in production for: +## https://github.com/erlang/otp/blob/feb45017da36be78d4c5784d758ede619fa7bfd3/erts/emulator/beam/erl_gc.c#L421 +## +## Value: Duration +## - h: hour +## - m: minute +## - s: second +## - ms: milliseconds +## +## Examples: +## - 2h: 2 hours +## - 30m: 30 minutes +## - 0.1s: 0.1 seconds +## - 100ms : 100 milliseconds +## +## Default: 0ms +sysmon.long_gc = 0 + +## Enable Long Schedule(ms) monitoring. +## +## See: http://erlang.org/doc/man/erlang.html#system_monitor-2 +## +## Value: Duration +## - h: hour +## - m: minute +## - s: second +## - ms: milliseconds +## +## Examples: +## - 2h: 2 hours +## - 30m: 30 minutes +## - 0.1s: 0.1 seconds +## - 100ms: 100 milliseconds +## +## Default: 0ms +sysmon.long_schedule = 240ms + +## Enable Large Heap monitoring. +## +## See: http://erlang.org/doc/man/erlang.html#system_monitor-2 +## +## Value: bytes +## +## Default: 8M words. 32MB on 32-bit VM, 64MB on 64-bit VM. +sysmon.large_heap = 8MB + +## Enable Busy Port monitoring. +## +## See: http://erlang.org/doc/man/erlang.html#system_monitor-2 +## +## Value: true | false +sysmon.busy_port = false + +## Enable Busy Dist Port monitoring. +## +## See: http://erlang.org/doc/man/erlang.html#system_monitor-2 +## +## Value: true | false +sysmon.busy_dist_port = true + +## The time interval for the periodic cpu check +## +## Value: Duration +## -h: hour, e.g. '2h' for 2 hours +## -m: minute, e.g. '5m' for 5 minutes +## -s: second, e.g. '30s' for 30 seconds +## +## Default: 60s +os_mon.cpu_check_interval = 60s + +## The threshold, as percentage of system cpu, for how much system cpu can be used before the corresponding alarm is set. +## +## Default: 80% +os_mon.cpu_high_watermark = 80% + +## The threshold, as percentage of system cpu, for how much system cpu can be used before the corresponding alarm is clear. +## +## Default: 60% +os_mon.cpu_low_watermark = 60% + +## The time interval for the periodic memory check +## +## Value: Duration +## -h: hour, e.g. '2h' for 2 hours +## -m: minute, e.g. '5m' for 5 minutes +## -s: second, e.g. '30s' for 30 seconds +## +## Default: 60s +os_mon.mem_check_interval = 60s + +## The threshold, as percentage of system memory, for how much system memory can be allocated before the corresponding alarm is set. +## +## Default: 70% +os_mon.sysmem_high_watermark = 70% + +## The threshold, as percentage of system memory, for how much system memory can be allocated by one Erlang process before the corresponding alarm is set. +## +## Default: 5% +os_mon.procmem_high_watermark = 5% + +## The time interval for the periodic process limit check +## +## Value: Duration +## +## Default: 30s +vm_mon.check_interval = 30s + +## The threshold, as percentage of processes, for how many processes can simultaneously exist at the local node before the corresponding alarm is set. +## +## Default: 80% +vm_mon.process_high_watermark = 80% + +## The threshold, as percentage of processes, for how many processes can simultaneously exist at the local node before the corresponding alarm is clear. +## +## Default: 60% +vm_mon.process_low_watermark = 60% + +## Specifies the actions to take when an alarm is activated +## +## Value: String +## - log +## - publish +## +## Default: log,publish +alarm.actions = log,publish + +## The maximum number of deactivated alarms +## +## Value: Integer +## +## Default: 1000 +alarm.size_limit = 1000 + +## Validity Period of deactivated alarms +## +## Value: Duration +## - h: hour +## - m: minute +## - s: second +## - ms: milliseconds +## +## Default: 24h +alarm.validity_period = 24h \ No newline at end of file diff --git a/etc/zones.conf b/etc/zones.conf new file mode 100644 index 000000000..ea1e1807c --- /dev/null +++ b/etc/zones.conf @@ -0,0 +1,327 @@ +##-------------------------------------------------------------------- +## Zones +##-------------------------------------------------------------------- + +##-------------------------------------------------------------------- +## External Zone + +## Idle timeout of the external MQTT connections. +## +## Value: duration +zone.external.idle_timeout = 15s + +## Enable ACL check. +## +## Value: Flag +zone.external.enable_acl = on + +## Enable ban check. +## +## Value: Flag +zone.external.enable_ban = on + +## Enable per connection statistics. +## +## Value: on | off +zone.external.enable_stats = on + +## The action when acl check reject current operation +## +## Value: ignore | disconnect +## Default: ignore +zone.external.acl_deny_action = ignore + +## Force the MQTT connection process GC after this number of +## messages | bytes passed through. +## +## Numbers delimited by `|'. Zero or negative is to disable. +zone.external.force_gc_policy = 16000|16MB + +## Max message queue length and total heap size to force shutdown +## connection/session process. +## Message queue here is the Erlang process mailbox, but not the number +## of queued MQTT messages of QoS 1 and 2. +## +## Numbers delimited by `|'. Zero or negative is to disable. +zone.external.force_shutdown_policy = 10000|32MB + +## Maximum MQTT packet size allowed. +## +## Value: Bytes +## Default: 1MB +## zone.external.max_packet_size = 64KB + +## Maximum length of MQTT clientId allowed. +## +## Value: Number [23-65535] +## zone.external.max_clientid_len = 1024 + +## Maximum topic levels allowed. 0 means no limit. +## +## Value: Number +## zone.external.max_topic_levels = 7 + +## Maximum QoS allowed. +## +## Value: 0 | 1 | 2 +## zone.external.max_qos_allowed = 2 + +## Maximum Topic Alias, 0 means no limit. +## +## Value: 0-65535 +## zone.external.max_topic_alias = 65535 + +## Whether the Server supports retained messages. +## +## Value: boolean +## zone.external.retain_available = true + +## Whether the Server supports Wildcard Subscriptions +## +## Value: boolean +## zone.external.wildcard_subscription = false + +## Whether the Server supports Shared Subscriptions +## +## Value: boolean +## zone.external.shared_subscription = false + +## Server Keep Alive +## +## Value: Number +## zone.external.server_keepalive = 0 + +## The backoff for MQTT keepalive timeout. The broker will kick a connection out +## until 'Keepalive * backoff * 2' timeout. +## +## Value: Float > 0.5 +zone.external.keepalive_backoff = 0.75 + +## Maximum number of subscriptions allowed, 0 means no limit. +## +## Value: Number +zone.external.max_subscriptions = 0 + +## Force to upgrade QoS according to subscription. +## +## Value: on | off +zone.external.upgrade_qos = off + +## Maximum size of the Inflight Window storing QoS1/2 messages delivered but unacked. +## +## Value: Number +zone.external.max_inflight = 32 + +## Retry interval for QoS1/2 message delivering. +## +## Value: Duration +zone.external.retry_interval = 30s + +## Maximum QoS2 packets (Client -> Broker) awaiting PUBREL, 0 means no limit. +## +## Value: Number +zone.external.max_awaiting_rel = 100 + +## The QoS2 messages (Client -> Broker) will be dropped if awaiting PUBREL timeout. +## +## Value: Duration +zone.external.await_rel_timeout = 300s + +## Default session expiry interval for MQTT V3.1.1 connections. +## +## Value: Duration +## -d: day +## -h: hour +## -m: minute +## -s: second +## +## Default: 2h, 2 hours +zone.external.session_expiry_interval = 2h + +## Maximum queue length. Enqueued messages when persistent client disconnected, +## or inflight window is full. 0 means no limit. +## +## Value: Number >= 0 +zone.external.max_mqueue_len = 1000 + +## Topic priorities. +## 'none' to indicate no priority table (by default), hence all messages +## are treated equal +## +## Priority number [1-255] +## Example: topic/1=10,topic/2=8 +## NOTE: comma and equal signs are not allowed for priority topic names +## NOTE: messages for topics not in the priority table are treated as +## either highest or lowest priority depending on the configured +## value for mqueue_default_priority +## +zone.external.mqueue_priorities = none + +## Default to highest priority for topics not matching priority table +## +## Value: highest | lowest +zone.external.mqueue_default_priority = highest + +## Whether to enqueue QoS0 messages. +## +## Value: false | true +zone.external.mqueue_store_qos0 = true + +## Whether to turn on flapping detect +## +## Value: on | off +zone.external.enable_flapping_detect = off + +## Message limit for the a external MQTT connection. +## +## Value: Number,Duration +## Example: 100 messages per 10 seconds. +#zone.external.rate_limit.conn_messages_in = 100,10s + +## Bytes limit for a external MQTT connections. +## +## Value: Number,Duration +## Example: 100KB incoming per 10 seconds. +#zone.external.rate_limit.conn_bytes_in = 100KB,10s + +## Messages quota for the each of external MQTT connection. +## This value consumed by the number of recipient on a message. +## +## Value: Number, Duration +## +## Example: 100 messaegs per 1s +#zone.external.quota.conn_messages_routing = 100,1s + +## Messages quota for the all of external MQTT connections. +## This value consumed by the number of recipient on a message. +## +## Value: Number, Duration +## +## Example: 200000 messaegs per 1s +#zone.external.quota.overall_messages_routing = 200000,1s + +## All the topics will be prefixed with the mountpoint path if this option is enabled. +## +## Variables in mountpoint path: +## - %c: clientid +## - %u: username +## +## Value: String +## zone.external.mountpoint = devicebound/ + +## Whether use username replace client id +## +## Value: boolean +## Default: false +zone.external.use_username_as_clientid = false + +## Whether to ignore loop delivery of messages.(for mqtt v3.1.1) +## +## Value: true | false +zone.external.ignore_loop_deliver = false + +## Whether to parse the MQTT frame in strict mode +## +## Value: true | false +zone.external.strict_mode = false + +## Specify the response information returned to the client +## +## Value: String +## zone.external.response_information = example + +##-------------------------------------------------------------------- +## Internal Zone + +zone.internal.allow_anonymous = true + +## Enable per connection stats. +## +## Value: Flag +zone.internal.enable_stats = on + +## Enable ACL check. +## +## Value: Flag +zone.internal.enable_acl = off + +## The action when acl check reject current operation +## +## Value: ignore | disconnect +## Default: ignore +zone.internal.acl_deny_action = ignore + +## See zone.$name.force_gc_policy +## zone.internal.force_gc_policy = 128000|128MB + +## See zone.$name.wildcard_subscription. +## +## Value: boolean +## zone.internal.wildcard_subscription = true + +## See zone.$name.shared_subscription. +## +## Value: boolean +## zone.internal.shared_subscription = true + +## See zone.$name.max_subscriptions. +## +## Value: Integer +zone.internal.max_subscriptions = 0 + +## See zone.$name.max_inflight +## +## Value: Number +zone.internal.max_inflight = 128 + +## See zone.$name.max_awaiting_rel +## +## Value: Number +zone.internal.max_awaiting_rel = 1000 + +## See zone.$name.max_mqueue_len +## +## Value: Number >= 0 +zone.internal.max_mqueue_len = 10000 + +## Whether to enqueue Qos0 messages. +## +## Value: false | true +zone.internal.mqueue_store_qos0 = true + +## Whether to turn on flapping detect +## +## Value: on | off +zone.internal.enable_flapping_detect = off + +## See zone.$name.force_shutdown_policy +zone.internal.force_shutdown_policy = 128000|128MB + +## All the topics will be prefixed with the mountpoint path if this option is enabled. +## +## Variables in mountpoint path: +## - %c: clientid +## - %u: username +## +## Value: String +## zone.internal.mountpoint = cloudbound/ + +## Whether to ignore loop delivery of messages.(for mqtt v3.1.1) +## +## Value: true | false +zone.internal.ignore_loop_deliver = false + +## Whether to parse the MQTT frame in strict mode +## +## Value: true | false +zone.internal.strict_mode = false + +## Specify the response information returned to the client +## +## Value: String +## zone.internal.response_information = example + +## Allow the zone's clients to bypass authentication step +## +## Value: true | false +zone.internal.bypass_auth_plugins = true diff --git a/priv/emqx.schema b/priv/emqx.schema index 7ca1f06ff..6f4e7012e 100644 --- a/priv/emqx.schema +++ b/priv/emqx.schema @@ -689,12 +689,6 @@ end}. {datatype, {enum, [allow, deny]}} ]}. -%% @doc Default ACL file. -{mapping, "acl_file", "emqx.acl_file", [ - {datatype, string}, - hidden -]}. - %% @doc Enable ACL cache for publish. {mapping, "enable_acl_cache", "emqx.enable_acl_cache", [ {default, on}, @@ -1026,6 +1020,7 @@ end}. %% of queued MQTT messages of QoS 1 and 2. %% Zero or negative is to disable. {mapping, "zone.$name.force_shutdown_policy", "emqx.zones", [ + {default, "default"}, {datatype, string} ]}. @@ -1082,6 +1077,17 @@ end}. count => list_to_integer(Count)} end, {force_gc_policy, GcPolicy}; + (["force_shutdown_policy"], "default") -> + {DefaultLen, DefaultSize} = + case WordSize = erlang:system_info(wordsize) of + 8 -> % arch_64 + {10000, cuttlefish_bytesize:parse("32MB")}; + 4 -> % arch_32 + {10000, cuttlefish_bytesize:parse("16MB")} + end, + {force_shutdown_policy, #{message_queue_len => DefaultLen, + max_heap_size => DefaultSize div WordSize + }}; (["force_shutdown_policy"], Val) -> [Len, Siz] = string:tokens(Val, "| "), MaxSiz = case WordSize = erlang:system_info(wordsize) of @@ -1551,6 +1557,10 @@ end}. hidden ]}. +{mapping, "listener.ws.$name.peer_cert_as_username", "emqx.listeners", [ + {datatype, {enum, [cn, dn, crt]}} +]}. + %%-------------------------------------------------------------------- %% MQTT/WebSocket/SSL Listeners @@ -1675,6 +1685,9 @@ end}. {mapping, "listener.wss.$name.cacertfile", "emqx.listeners", [ {datatype, string} ]}. +{mapping, "listener.wss.$name.dhfile", "emqx.listeners", [ + {datatype, string} +]}. {mapping, "listener.wss.$name.verify", "emqx.listeners", [ {datatype, atom} @@ -1915,93 +1928,6 @@ end}. ++ cuttlefish_variable:filter_by_prefix("listener.wss", Conf)]) end}. -%%-------------------------------------------------------------------- -%% Modules -%%-------------------------------------------------------------------- - -{mapping, "modules.loaded_file", "emqx.modules_loaded_file", [ - {datatype, string} -]}. - -{mapping, "module.presence.qos", "emqx.modules", [ - {default, 1}, - {datatype, integer}, - {validators, ["range:0-2"]} -]}. - -{mapping, "module.subscription.$id.topic", "emqx.modules", [ - {datatype, string} -]}. - -{mapping, "module.subscription.$id.qos", "emqx.modules", [ - {default, 1}, - {datatype, integer}, - {validators, ["range:0-2"]} -]}. - -{mapping, "module.subscription.$id.nl", "emqx.modules", [ - {default, 0}, - {datatype, integer}, - {validators, ["range:0-1"]} -]}. - -{mapping, "module.subscription.$id.rap", "emqx.modules", [ - {default, 0}, - {datatype, integer}, - {validators, ["range:0-1"]} -]}. - -{mapping, "module.subscription.$id.rh", "emqx.modules", [ - {default, 0}, - {datatype, integer}, - {validators, ["range:0-2"]} -]}. - -{mapping, "module.rewrite.rule.$id", "emqx.modules", [ - {datatype, string} -]}. - -{mapping, "module.rewrite.pub.rule.$id", "emqx.modules", [ - {datatype, string} -]}. - -{mapping, "module.rewrite.sub.rule.$id", "emqx.modules", [ - {datatype, string} -]}. - -{translation, "emqx.modules", fun(Conf, _, Conf1) -> - Subscriptions = fun() -> - List = cuttlefish_variable:filter_by_prefix("module.subscription", Conf), - TopicList = [{N, Topic}|| {[_,"subscription",N,"topic"], Topic} <- List], - [{iolist_to_binary(T), #{ qos => cuttlefish:conf_get("module.subscription." ++ N ++ ".qos", Conf, 0), - nl => cuttlefish:conf_get("module.subscription." ++ N ++ ".nl", Conf, 0), - rap => cuttlefish:conf_get("module.subscription." ++ N ++ ".rap", Conf, 0), - rh => cuttlefish:conf_get("module.subscription." ++ N ++ ".rh", Conf, 0) - }} || {N, T} <- TopicList] - end, - Rewrites = fun() -> - Rules = cuttlefish_variable:filter_by_prefix("module.rewrite.rule", Conf), - PubRules = cuttlefish_variable:filter_by_prefix("module.rewrite.pub.rule", Conf), - SubRules = cuttlefish_variable:filter_by_prefix("module.rewrite.sub.rule", Conf), - TotalRules = lists:append( - [ {["module", "rewrite", "pub", "rule", I], Rule} || {["module", "rewrite", "rule", I], Rule} <- Rules] ++ PubRules, - [ {["module", "rewrite", "sub", "rule", I], Rule} || {["module", "rewrite", "rule", I], Rule} <- Rules] ++ SubRules - ), - lists:map(fun({[_, "rewrite", PubOrSub, "rule", I], Rule}) -> - [Topic, Re, Dest] = string:tokens(Rule, " "), - {rewrite, list_to_atom(PubOrSub), list_to_binary(Topic), list_to_binary(Re), list_to_binary(Dest)} - end, TotalRules) - end, - lists:append([ - [{emqx_mod_presence, [{qos, cuttlefish:conf_get("module.presence.qos", Conf, 1)}]}], - [{emqx_mod_subscription, Subscriptions()}], - [{emqx_mod_rewrite, Rewrites()}], - [{emqx_mod_topic_metrics, []}], - [{emqx_mod_delayed, []}], - [{emqx_mod_acl_internal, [{acl_file, cuttlefish:conf_get("acl_file", Conf1)}]}] - ]) -end}. - %%------------------------------------------------------------------- %% Plugins %%------------------------------------------------------------------- diff --git a/rebar.config b/rebar.config index 048a89e98..be7d01148 100644 --- a/rebar.config +++ b/rebar.config @@ -6,7 +6,7 @@ [{gproc, {git, "https://github.com/uwiger/gproc", {tag, "0.8.0"}}}, {jiffy, {git, "https://github.com/emqx/jiffy", {tag, "1.0.5"}}}, {cowboy, {git, "https://github.com/emqx/cowboy", {tag, "2.7.1"}}}, - {esockd, {git, "https://github.com/emqx/esockd", {tag, "5.7.2"}}}, + {esockd, {git, "https://github.com/emqx/esockd", {tag, "5.7.3"}}}, {ekka, {git, "https://github.com/emqx/ekka", {tag, "0.7.4"}}}, {gen_rpc, {git, "https://github.com/emqx/gen_rpc", {tag, "2.5.0"}}}, {cuttlefish, {git, "https://github.com/emqx/cuttlefish", {tag, "v3.0.0"}}} diff --git a/src/emqx_access_rule.erl b/src/emqx_access_rule.erl deleted file mode 100644 index 89943cdbd..000000000 --- a/src/emqx_access_rule.erl +++ /dev/null @@ -1,155 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_access_rule). - --include("emqx.hrl"). - -%% APIs --export([ match/3 - , compile/1 - ]). - --export_type([rule/0]). - --type(acl_result() :: allow | deny). - --type(who() :: all | binary() | - {client, binary()} | - {user, binary()} | - {ipaddr, esockd_cidr:cidr_string()}). - --type(access() :: subscribe | publish | pubsub). - --type(rule() :: {acl_result(), all} | - {acl_result(), who(), access(), list(emqx_topic:topic())}). - --define(ALLOW_DENY(A), ((A =:= allow) orelse (A =:= deny))). --define(PUBSUB(A), ((A =:= subscribe) orelse (A =:= publish) orelse (A =:= pubsub))). - -%% @doc Compile Access Rule. -compile({A, all}) when ?ALLOW_DENY(A) -> - {A, all}; - -compile({A, Who, Access, Topic}) when ?ALLOW_DENY(A), ?PUBSUB(Access), is_binary(Topic) -> - {A, compile(who, Who), Access, [compile(topic, Topic)]}; - -compile({A, Who, Access, TopicFilters}) when ?ALLOW_DENY(A), ?PUBSUB(Access) -> - {A, compile(who, Who), Access, [compile(topic, Topic) || Topic <- TopicFilters]}. - -compile(who, all) -> - all; -compile(who, {ipaddr, CIDR}) -> - {ipaddr, esockd_cidr:parse(CIDR, true)}; -compile(who, {client, all}) -> - {client, all}; -compile(who, {client, ClientId}) -> - {client, bin(ClientId)}; -compile(who, {user, all}) -> - {user, all}; -compile(who, {user, Username}) -> - {user, bin(Username)}; -compile(who, {'and', Conds}) when is_list(Conds) -> - {'and', [compile(who, Cond) || Cond <- Conds]}; -compile(who, {'or', Conds}) when is_list(Conds) -> - {'or', [compile(who, Cond) || Cond <- Conds]}; - -compile(topic, {eq, Topic}) -> - {eq, emqx_topic:words(bin(Topic))}; -compile(topic, Topic) -> - Words = emqx_topic:words(bin(Topic)), - case 'pattern?'(Words) of - true -> {pattern, Words}; - false -> Words - end. - -'pattern?'(Words) -> - lists:member(<<"%u">>, Words) - orelse lists:member(<<"%c">>, Words). - -bin(L) when is_list(L) -> - list_to_binary(L); -bin(B) when is_binary(B) -> - B. - -%% @doc Match access rule --spec(match(emqx_types:clientinfo(), emqx_types:topic(), rule()) - -> {matched, allow} | {matched, deny} | nomatch). -match(_ClientInfo, _Topic, {AllowDeny, all}) when ?ALLOW_DENY(AllowDeny) -> - {matched, AllowDeny}; -match(ClientInfo, Topic, {AllowDeny, Who, _PubSub, TopicFilters}) - when ?ALLOW_DENY(AllowDeny) -> - case match_who(ClientInfo, Who) - andalso match_topics(ClientInfo, Topic, TopicFilters) of - true -> {matched, AllowDeny}; - false -> nomatch - end. - -match_who(_ClientInfo, all) -> - true; -match_who(_ClientInfo, {user, all}) -> - true; -match_who(_ClientInfo, {client, all}) -> - true; -match_who(#{clientid := ClientId}, {client, ClientId}) -> - true; -match_who(#{username := Username}, {user, Username}) -> - true; -match_who(#{peerhost := undefined}, {ipaddr, _Tup}) -> - false; -match_who(#{peerhost := IP}, {ipaddr, CIDR}) -> - esockd_cidr:match(IP, CIDR); -match_who(ClientInfo, {'and', Conds}) when is_list(Conds) -> - lists:foldl(fun(Who, Allow) -> - match_who(ClientInfo, Who) andalso Allow - end, true, Conds); -match_who(ClientInfo, {'or', Conds}) when is_list(Conds) -> - lists:foldl(fun(Who, Allow) -> - match_who(ClientInfo, Who) orelse Allow - end, false, Conds); -match_who(_ClientInfo, _Who) -> - false. - -match_topics(_ClientInfo, _Topic, []) -> - false; -match_topics(ClientInfo, Topic, [{pattern, PatternFilter}|Filters]) -> - TopicFilter = feed_var(ClientInfo, PatternFilter), - match_topic(emqx_topic:words(Topic), TopicFilter) - orelse match_topics(ClientInfo, Topic, Filters); -match_topics(ClientInfo, Topic, [TopicFilter|Filters]) -> - match_topic(emqx_topic:words(Topic), TopicFilter) - orelse match_topics(ClientInfo, Topic, Filters). - -match_topic(Topic, {eq, TopicFilter}) -> - Topic == TopicFilter; -match_topic(Topic, TopicFilter) -> - emqx_topic:match(Topic, TopicFilter). - -feed_var(ClientInfo, Pattern) -> - feed_var(ClientInfo, Pattern, []). -feed_var(_ClientInfo, [], Acc) -> - lists:reverse(Acc); -feed_var(ClientInfo = #{clientid := undefined}, [<<"%c">>|Words], Acc) -> - feed_var(ClientInfo, Words, [<<"%c">>|Acc]); -feed_var(ClientInfo = #{clientid := ClientId}, [<<"%c">>|Words], Acc) -> - feed_var(ClientInfo, Words, [ClientId |Acc]); -feed_var(ClientInfo = #{username := undefined}, [<<"%u">>|Words], Acc) -> - feed_var(ClientInfo, Words, [<<"%u">>|Acc]); -feed_var(ClientInfo = #{username := Username}, [<<"%u">>|Words], Acc) -> - feed_var(ClientInfo, Words, [Username|Acc]); -feed_var(ClientInfo, [W|Words], Acc) -> - feed_var(ClientInfo, Words, [W|Acc]). - diff --git a/src/emqx_alarm.erl b/src/emqx_alarm.erl index 510de9a4c..37d6fe1e8 100644 --- a/src/emqx_alarm.erl +++ b/src/emqx_alarm.erl @@ -200,10 +200,10 @@ handle_call({deactivate_alarm, Name}, _From, State = #state{actions = Actions, ok end, Alarm = #deactivated_alarm{activate_at = ActivateAt, - name = Name, - details = Details, - message = Message, - deactivate_at = erlang:system_time(microsecond)}, + name = Name, + details = Details, + message = Message, + deactivate_at = erlang:system_time(microsecond)}, mnesia:dirty_delete(?ACTIVATED_ALARM, Name), mnesia:dirty_write(?DEACTIVATED_ALARM, Alarm), do_actions(deactivate, Alarm, Actions), diff --git a/src/emqx_app.erl b/src/emqx_app.erl index 689a1b69e..881a1ee99 100644 --- a/src/emqx_app.erl +++ b/src/emqx_app.erl @@ -32,12 +32,11 @@ start(_Type, _Args) -> print_banner(), ekka:start(), {ok, Sup} = emqx_sup:start_link(), - ok = emqx_modules:load(), - ok = emqx_plugins:init(), - emqx_plugins:load(), + start_autocluster(), emqx_boot:is_enabled(listeners) andalso (ok = emqx_listeners:start()), - start_autocluster(), + ok = emqx_plugins:init(), + emqx_plugins:load(), register(emqx, self()), emqx_alarm_handler:load(), print_vsn(), @@ -47,8 +46,7 @@ start(_Type, _Args) -> stop(_State) -> emqx_alarm_handler:unload(), emqx_boot:is_enabled(listeners) - andalso emqx_listeners:stop(), - emqx_modules:unload(). + andalso emqx_listeners:stop(). %%-------------------------------------------------------------------- %% Print Banner diff --git a/src/emqx_channel.erl b/src/emqx_channel.erl index 2a3e35ccd..289ed50f0 100644 --- a/src/emqx_channel.erl +++ b/src/emqx_channel.erl @@ -648,17 +648,21 @@ maybe_update_expiry_interval(_Properties, Channel) -> Channel. -spec(handle_deliver(list(emqx_types:deliver()), channel()) -> {ok, channel()} | {ok, replies(), channel()}). handle_deliver(Delivers, Channel = #channel{conn_state = disconnected, - session = Session}) -> - NSession = emqx_session:enqueue(maybe_nack(Delivers), Session), + session = Session, + clientinfo = #{clientid := ClientId}}) -> + NSession = emqx_session:enqueue(ignore_local(maybe_nack(Delivers), ClientId, Session), Session), {ok, Channel#channel{session = NSession}}; handle_deliver(Delivers, Channel = #channel{takeover = true, - pendings = Pendings}) -> - NPendings = lists:append(Pendings, maybe_nack(Delivers)), + pendings = Pendings, + session = Session, + clientinfo = #{clientid := ClientId}}) -> + NPendings = lists:append(Pendings, ignore_local(maybe_nack(Delivers), ClientId, Session)), {ok, Channel#channel{pendings = NPendings}}; -handle_deliver(Delivers, Channel = #channel{session = Session}) -> - case emqx_session:deliver(Delivers, Session) of +handle_deliver(Delivers, Channel = #channel{session = Session, + clientinfo = #{clientid := ClientId}}) -> + case emqx_session:deliver(ignore_local(Delivers, ClientId, Session), Session) of {ok, Publishes, NSession} -> NChannel = Channel#channel{session = NSession}, handle_out(publish, Publishes, ensure_timer(retry_timer, NChannel)); @@ -666,6 +670,19 @@ handle_deliver(Delivers, Channel = #channel{session = Session}) -> {ok, Channel#channel{session = NSession}} end. +ignore_local(Delivers, Subscriber, Session) -> + Subs = emqx_session:info(subscriptions, Session), + lists:dropwhile(fun({deliver, Topic, #message{from = Publisher}}) -> + case maps:find(Topic, Subs) of + {ok, #{nl := 1}} when Subscriber =:= Publisher -> + ok = emqx_metrics:inc('delivery.dropped'), + ok = emqx_metrics:inc('delivery.dropped.no_local'), + true; + _ -> + false + end + end, Delivers). + %% Nack delivers from shared subscription maybe_nack(Delivers) -> lists:filter(fun not_nacked/1, Delivers). @@ -782,22 +799,15 @@ do_deliver({pubrel, PacketId}, Channel) -> do_deliver({PacketId, Msg}, Channel = #channel{clientinfo = ClientInfo = #{mountpoint := MountPoint}}) -> - case ignore_local(Msg, ClientInfo) of - true -> - ok = emqx_metrics:inc('delivery.dropped'), - ok = emqx_metrics:inc('delivery.dropped.no_local'), - {[], Channel}; - false -> - ok = emqx_metrics:inc('messages.delivered'), - Msg1 = emqx_hooks:run_fold('message.delivered', - [ClientInfo], - emqx_message:update_expiry(Msg) - ), - Msg2 = emqx_mountpoint:unmount(MountPoint, Msg1), - Packet = emqx_message:to_packet(PacketId, Msg2), - {NPacket, NChannel} = packing_alias(Packet, Channel), - {[NPacket], NChannel} - end; + ok = emqx_metrics:inc('messages.delivered'), + Msg1 = emqx_hooks:run_fold('message.delivered', + [ClientInfo], + emqx_message:update_expiry(Msg) + ), + Msg2 = emqx_mountpoint:unmount(MountPoint, Msg1), + Packet = emqx_message:to_packet(PacketId, Msg2), + {NPacket, NChannel} = packing_alias(Packet, Channel), + {[NPacket], NChannel}; do_deliver([Publish], Channel) -> do_deliver(Publish, Channel); @@ -810,11 +820,6 @@ do_deliver(Publishes, Channel) when is_list(Publishes) -> end, {[], Channel}, Publishes), {lists:reverse(Packets), NChannel}. -ignore_local(#message{flags = #{nl := true}, from = ClientId}, - #{clientid := ClientId}) -> - true; -ignore_local(_Msg, _ClientInfo) -> false. - %%-------------------------------------------------------------------- %% Handle out suback %%-------------------------------------------------------------------- diff --git a/src/emqx_cm_locker.erl b/src/emqx_cm_locker.erl index 708d3d23c..b5979c706 100644 --- a/src/emqx_cm_locker.erl +++ b/src/emqx_cm_locker.erl @@ -60,7 +60,7 @@ lock(ClientId, Piggyback) -> unlock(ClientId) -> ekka_locker:release(?MODULE, ClientId, strategy()). --spec(strategy() -> local | one | quorum | all). +-spec(strategy() -> local | leader | quorum | all). strategy() -> emqx:get_env(session_locking_strategy, quorum). diff --git a/src/emqx_gen_mod.erl b/src/emqx_gen_mod.erl deleted file mode 100644 index 26a3c13cb..000000000 --- a/src/emqx_gen_mod.erl +++ /dev/null @@ -1,23 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_gen_mod). - --callback(load(Opts :: any()) -> ok | {error, term()}). - --callback(unload(State :: term()) -> term()). - --callback(description() -> any()). diff --git a/src/emqx_mod_acl_internal.erl b/src/emqx_mod_acl_internal.erl deleted file mode 100644 index 7d5dd82c6..000000000 --- a/src/emqx_mod_acl_internal.erl +++ /dev/null @@ -1,122 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_mod_acl_internal). - --behaviour(emqx_gen_mod). - --include("emqx.hrl"). --include("logger.hrl"). - --logger_header("[ACL_INTERNAL]"). - -%% APIs --export([ check_acl/5 - , rules_from_file/1 - ]). - -%% emqx_gen_mod callbacks --export([ load/1 - , unload/1 - , reload/1 - , description/0 - ]). - --type(acl_rules() :: #{publish => [emqx_access_rule:rule()], - subscribe => [emqx_access_rule:rule()]}). - -%%-------------------------------------------------------------------- -%% API -%%-------------------------------------------------------------------- - -load(Env) -> - Rules = rules_from_file(proplists:get_value(acl_file, Env)), - emqx_hooks:add('client.check_acl', {?MODULE, check_acl, [Rules]}, -1). - -unload(_Env) -> - emqx_hooks:del('client.check_acl', {?MODULE, check_acl}). - -reload(Env) -> - emqx_acl_cache:is_enabled() andalso ( - lists:foreach( - fun(Pid) -> erlang:send(Pid, clean_acl_cache) end, - emqx_cm:all_channels())), - unload(Env), load(Env). - -description() -> - "EMQ X Internal ACL Module". -%%-------------------------------------------------------------------- -%% ACL callbacks -%%-------------------------------------------------------------------- - -%% @doc Check ACL --spec(check_acl(emqx_types:clientinfo(), emqx_types:pubsub(), emqx_topic:topic(), - emqx_access_rule:acl_result(), acl_rules()) - -> {ok, allow} | {ok, deny} | ok). -check_acl(Client, PubSub, Topic, _AclResult, Rules) -> - case match(Client, Topic, lookup(PubSub, Rules)) of - {matched, allow} -> {ok, allow}; - {matched, deny} -> {ok, deny}; - nomatch -> ok - end. - -%%-------------------------------------------------------------------- -%% Internal Functions -%%-------------------------------------------------------------------- -lookup(PubSub, Rules) -> - maps:get(PubSub, Rules, []). - -match(_Client, _Topic, []) -> - nomatch; -match(Client, Topic, [Rule|Rules]) -> - case emqx_access_rule:match(Client, Topic, Rule) of - nomatch -> - match(Client, Topic, Rules); - {matched, AllowDeny} -> - {matched, AllowDeny} - end. - --spec(rules_from_file(file:filename()) -> map()). -rules_from_file(AclFile) -> - case file:consult(AclFile) of - {ok, Terms} -> - Rules = [emqx_access_rule:compile(Term) || Term <- Terms], - #{publish => [Rule || Rule <- Rules, filter(publish, Rule)], - subscribe => [Rule || Rule <- Rules, filter(subscribe, Rule)]}; - {error, eacces} -> - ?LOG(alert, "Insufficient permissions to read the ~s file", [AclFile]), - #{}; - {error, enoent} -> - ?LOG(alert, "The ~s file does not exist", [AclFile]), - #{}; - {error, Reason} -> - ?LOG(alert, "Failed to read ~s: ~p", [AclFile, Reason]), - #{} - end. - -filter(_PubSub, {allow, all}) -> - true; -filter(_PubSub, {deny, all}) -> - true; -filter(publish, {_AllowDeny, _Who, publish, _Topics}) -> - true; -filter(_PubSub, {_AllowDeny, _Who, pubsub, _Topics}) -> - true; -filter(subscribe, {_AllowDeny, _Who, subscribe, _Topics}) -> - true; -filter(_PubSub, {_AllowDeny, _Who, _, _Topics}) -> - false. - diff --git a/src/emqx_mod_delayed.erl b/src/emqx_mod_delayed.erl deleted file mode 100644 index 208da68ca..000000000 --- a/src/emqx_mod_delayed.erl +++ /dev/null @@ -1,204 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_mod_delayed). - --behaviour(gen_server). --behaviour(emqx_gen_mod). - --include_lib("emqx/include/emqx.hrl"). --include_lib("emqx/include/logger.hrl"). - -%% Mnesia bootstrap --export([mnesia/1]). - --boot_mnesia({mnesia, [boot]}). --copy_mnesia({mnesia, [copy]}). - -%% emqx_gen_mod callbacks --export([ load/1 - , unload/1 - , description/0 - ]). - --export([ start_link/0 - , on_message_publish/1 - ]). - -%% gen_server callbacks --export([ init/1 - , handle_call/3 - , handle_cast/2 - , handle_info/2 - , terminate/2 - , code_change/3 - ]). - --record(delayed_message, - { key - , msg - }). - --define(TAB, ?MODULE). --define(SERVER, ?MODULE). --define(MAX_INTERVAL, 4294967). - -%%-------------------------------------------------------------------- -%% Mnesia bootstrap -%%-------------------------------------------------------------------- - -mnesia(boot) -> - ok = ekka_mnesia:create_table(?TAB, [ - {type, ordered_set}, - {disc_copies, [node()]}, - {local_content, true}, - {record_name, delayed_message}, - {attributes, record_info(fields, delayed_message)}]); -mnesia(copy) -> - ok = ekka_mnesia:copy_table(?TAB, disc_copies). - -%%-------------------------------------------------------------------- -%% Load/Unload -%%-------------------------------------------------------------------- - --spec(load(list()) -> ok). -load(_Env) -> - emqx_mod_sup:start_child(?MODULE, worker), - emqx:hook('message.publish', {?MODULE, on_message_publish, []}). - --spec(unload(list()) -> ok). -unload(_Env) -> - emqx:unhook('message.publish', {?MODULE, on_message_publish}), - emqx_mod_sup:stop_child(?MODULE). - -description() -> - "EMQ X Delayed Publish Module". -%%-------------------------------------------------------------------- -%% Hooks -%%-------------------------------------------------------------------- - -on_message_publish(Msg = #message{id = Id, topic = <<"$delayed/", Topic/binary>>, timestamp = Ts}) -> - [Delay, Topic1] = binary:split(Topic, <<"/">>), - PubAt = case binary_to_integer(Delay) of - Interval when Interval < ?MAX_INTERVAL -> - Interval + erlang:round(Ts / 1000); - Timestamp -> - %% Check malicious timestamp? - case (Timestamp - erlang:round(Ts / 1000)) > ?MAX_INTERVAL of - true -> error(invalid_delayed_timestamp); - false -> Timestamp - end - end, - PubMsg = Msg#message{topic = Topic1}, - Headers = PubMsg#message.headers, - ok = store(#delayed_message{key = {PubAt, Id}, msg = PubMsg}), - {stop, PubMsg#message{headers = Headers#{allow_publish => false}}}; - -on_message_publish(Msg) -> - {ok, Msg}. - -%%-------------------------------------------------------------------- -%% Start delayed publish server -%%-------------------------------------------------------------------- - --spec(start_link() -> emqx_types:startlink_ret()). -start_link() -> - gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). - --spec(store(#delayed_message{}) -> ok). -store(DelayedMsg) -> - gen_server:call(?SERVER, {store, DelayedMsg}, infinity). - -%%-------------------------------------------------------------------- -%% gen_server callback -%%-------------------------------------------------------------------- - -init([]) -> - {ok, ensure_publish_timer(#{timer => undefined, publish_at => 0})}. - -handle_call({store, DelayedMsg = #delayed_message{key = Key}}, _From, State) -> - ok = mnesia:dirty_write(?TAB, DelayedMsg), - emqx_metrics:set('messages.delayed', delayed_count()), - {reply, ok, ensure_publish_timer(Key, State)}; - -handle_call(Req, _From, State) -> - ?LOG(error, "[Delayed] Unexpected call: ~p", [Req]), - {reply, ignored, State}. - -handle_cast(Msg, State) -> - ?LOG(error, "[Delayed] Unexpected cast: ~p", [Msg]), - {noreply, State}. - -%% Do Publish... -handle_info({timeout, TRef, do_publish}, State = #{timer := TRef}) -> - DeletedKeys = do_publish(mnesia:dirty_first(?TAB), os:system_time(seconds)), - lists:foreach(fun(Key) -> mnesia:dirty_delete(?TAB, Key) end, DeletedKeys), - emqx_metrics:set('messages.delayed', delayed_count()), - {noreply, ensure_publish_timer(State#{timer := undefined, publish_at := 0})}; - -handle_info(Info, State) -> - ?LOG(error, "[Delayed] Unexpected info: ~p", [Info]), - {noreply, State}. - -terminate(_Reason, #{timer := TRef}) -> - emqx_misc:cancel_timer(TRef). - -code_change(_OldVsn, State, _Extra) -> - {ok, State}. - -%%-------------------------------------------------------------------- -%% Internal functions -%%-------------------------------------------------------------------- - -%% Ensure publish timer -ensure_publish_timer(State) -> - ensure_publish_timer(mnesia:dirty_first(?TAB), State). - -ensure_publish_timer('$end_of_table', State) -> - State#{timer := undefined, publish_at := 0}; -ensure_publish_timer({Ts, _Id}, State = #{timer := undefined}) -> - ensure_publish_timer(Ts, os:system_time(seconds), State); -ensure_publish_timer({Ts, _Id}, State = #{timer := TRef, publish_at := PubAt}) - when Ts < PubAt -> - ok = emqx_misc:cancel_timer(TRef), - ensure_publish_timer(Ts, os:system_time(seconds), State); -ensure_publish_timer(_Key, State) -> - State. - -ensure_publish_timer(Ts, Now, State) -> - Interval = max(1, Ts - Now), - TRef = emqx_misc:start_timer(timer:seconds(Interval), do_publish), - State#{timer := TRef, publish_at := Now + Interval}. - -do_publish(Key, Now) -> - do_publish(Key, Now, []). - -%% Do publish -do_publish('$end_of_table', _Now, Acc) -> - Acc; -do_publish({Ts, _Id}, Now, Acc) when Ts > Now -> - Acc; -do_publish(Key = {Ts, _Id}, Now, Acc) when Ts =< Now -> - case mnesia:dirty_read(?TAB, Key) of - [] -> ok; - [#delayed_message{msg = Msg}] -> - emqx_pool:async_submit(fun emqx:publish/1, [Msg]) - end, - do_publish(mnesia:dirty_next(?TAB, Key), Now, [Key|Acc]). - --spec(delayed_count() -> non_neg_integer()). -delayed_count() -> mnesia:table_info(?TAB, size). - diff --git a/src/emqx_mod_presence.erl b/src/emqx_mod_presence.erl deleted file mode 100644 index f5aa2279e..000000000 --- a/src/emqx_mod_presence.erl +++ /dev/null @@ -1,130 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_mod_presence). - --behaviour(emqx_gen_mod). - --include("emqx.hrl"). --include("logger.hrl"). - --logger_header("[Presence]"). - -%% emqx_gen_mod callbacks --export([ load/1 - , unload/1 - , description/0 - ]). - --export([ on_client_connected/3 - , on_client_disconnected/4 - ]). - --ifdef(TEST). --export([reason/1]). --endif. - -load(Env) -> - emqx_hooks:add('client.connected', {?MODULE, on_client_connected, [Env]}), - emqx_hooks:add('client.disconnected', {?MODULE, on_client_disconnected, [Env]}). - -unload(_Env) -> - emqx_hooks:del('client.connected', {?MODULE, on_client_connected}), - emqx_hooks:del('client.disconnected', {?MODULE, on_client_disconnected}). - -description() -> - "EMQ X Presence Module". -%%-------------------------------------------------------------------- -%% Callbacks -%%-------------------------------------------------------------------- - -on_client_connected(ClientInfo = #{clientid := ClientId}, ConnInfo, Env) -> - Presence = connected_presence(ClientInfo, ConnInfo), - case emqx_json:safe_encode(Presence) of - {ok, Payload} -> - emqx_broker:safe_publish( - make_msg(qos(Env), topic(connected, ClientId), Payload)); - {error, _Reason} -> - ?LOG(error, "Failed to encode 'connected' presence: ~p", [Presence]) - end. - -on_client_disconnected(_ClientInfo = #{clientid := ClientId, username := Username}, - Reason, _ConnInfo = #{disconnected_at := DisconnectedAt}, Env) -> - Presence = #{clientid => ClientId, - username => Username, - reason => reason(Reason), - disconnected_at => DisconnectedAt, - ts => erlang:system_time(millisecond) - }, - case emqx_json:safe_encode(Presence) of - {ok, Payload} -> - emqx_broker:safe_publish( - make_msg(qos(Env), topic(disconnected, ClientId), Payload)); - {error, _Reason} -> - ?LOG(error, "Failed to encode 'disconnected' presence: ~p", [Presence]) - end. - -%%-------------------------------------------------------------------- -%% Helper functions -%%-------------------------------------------------------------------- - -connected_presence(#{peerhost := PeerHost, - sockport := SockPort, - clientid := ClientId, - username := Username - }, - #{clean_start := CleanStart, - proto_name := ProtoName, - proto_ver := ProtoVer, - keepalive := Keepalive, - connected_at := ConnectedAt, - expiry_interval := ExpiryInterval - }) -> - #{clientid => ClientId, - username => Username, - ipaddress => ntoa(PeerHost), - sockport => SockPort, - proto_name => ProtoName, - proto_ver => ProtoVer, - keepalive => Keepalive, - connack => 0, %% Deprecated? - clean_start => CleanStart, - expiry_interval => ExpiryInterval, - connected_at => ConnectedAt, - ts => erlang:system_time(millisecond) - }. - -make_msg(QoS, Topic, Payload) -> - emqx_message:set_flag( - sys, emqx_message:make( - ?MODULE, QoS, Topic, iolist_to_binary(Payload))). - -topic(connected, ClientId) -> - emqx_topic:systop(iolist_to_binary(["clients/", ClientId, "/connected"])); -topic(disconnected, ClientId) -> - emqx_topic:systop(iolist_to_binary(["clients/", ClientId, "/disconnected"])). - -qos(Env) -> proplists:get_value(qos, Env, 0). - --compile({inline, [reason/1]}). -reason(Reason) when is_atom(Reason) -> Reason; -reason({shutdown, Reason}) when is_atom(Reason) -> Reason; -reason({Error, _}) when is_atom(Error) -> Error; -reason(_) -> internal_error. - --compile({inline, [ntoa/1]}). -ntoa(IpAddr) -> iolist_to_binary(inet:ntoa(IpAddr)). - diff --git a/src/emqx_mod_rewrite.erl b/src/emqx_mod_rewrite.erl deleted file mode 100644 index f5e343eed..000000000 --- a/src/emqx_mod_rewrite.erl +++ /dev/null @@ -1,103 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_mod_rewrite). - --behaviour(emqx_gen_mod). - --include_lib("emqx.hrl"). --include_lib("emqx_mqtt.hrl"). - --ifdef(TEST). --export([ compile/1 - , match_and_rewrite/2 - ]). --endif. - -%% APIs --export([ rewrite_subscribe/4 - , rewrite_unsubscribe/4 - , rewrite_publish/2 - ]). - -%% emqx_gen_mod callbacks --export([ load/1 - , unload/1 - , description/0 - ]). - -%%-------------------------------------------------------------------- -%% Load/Unload -%%-------------------------------------------------------------------- - -load(RawRules) -> - {PubRules, SubRules} = compile(RawRules), - emqx_hooks:add('client.subscribe', {?MODULE, rewrite_subscribe, [SubRules]}), - emqx_hooks:add('client.unsubscribe', {?MODULE, rewrite_unsubscribe, [SubRules]}), - emqx_hooks:add('message.publish', {?MODULE, rewrite_publish, [PubRules]}). - -rewrite_subscribe(_ClientInfo, _Properties, TopicFilters, Rules) -> - {ok, [{match_and_rewrite(Topic, Rules), Opts} || {Topic, Opts} <- TopicFilters]}. - -rewrite_unsubscribe(_ClientInfo, _Properties, TopicFilters, Rules) -> - {ok, [{match_and_rewrite(Topic, Rules), Opts} || {Topic, Opts} <- TopicFilters]}. - -rewrite_publish(Message = #message{topic = Topic}, Rules) -> - {ok, Message#message{topic = match_and_rewrite(Topic, Rules)}}. - -unload(_) -> - emqx_hooks:del('client.subscribe', {?MODULE, rewrite_subscribe}), - emqx_hooks:del('client.unsubscribe', {?MODULE, rewrite_unsubscribe}), - emqx_hooks:del('message.publish', {?MODULE, rewrite_publish}). - -description() -> - "EMQ X Topic Rewrite Module". -%%-------------------------------------------------------------------- -%% Internal functions -%%-------------------------------------------------------------------- - -compile(Rules) -> - PubRules = [ begin - {ok, MP} = re:compile(Re), - {rewrite, Topic, MP, Dest} - end || {rewrite, pub, Topic, Re, Dest}<- Rules ], - SubRules = [ begin - {ok, MP} = re:compile(Re), - {rewrite, Topic, MP, Dest} - end || {rewrite, sub, Topic, Re, Dest}<- Rules ], - {PubRules, SubRules}. - -match_and_rewrite(Topic, []) -> - Topic; - -match_and_rewrite(Topic, [{rewrite, Filter, MP, Dest} | Rules]) -> - case emqx_topic:match(Topic, Filter) of - true -> rewrite(Topic, MP, Dest); - false -> match_and_rewrite(Topic, Rules) - end. - -rewrite(Topic, MP, Dest) -> - case re:run(Topic, MP, [{capture, all_but_first, list}]) of - {match, Captured} -> - Vars = lists:zip(["\\$" ++ integer_to_list(I) - || I <- lists:seq(1, length(Captured))], Captured), - iolist_to_binary(lists:foldl( - fun({Var, Val}, Acc) -> - re:replace(Acc, Var, Val, [global]) - end, Dest, Vars)); - nomatch -> Topic - end. - diff --git a/src/emqx_mod_subscription.erl b/src/emqx_mod_subscription.erl deleted file mode 100644 index b6d04528b..000000000 --- a/src/emqx_mod_subscription.erl +++ /dev/null @@ -1,65 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_mod_subscription). - --behaviour(emqx_gen_mod). - --include_lib("emqx.hrl"). --include_lib("emqx_mqtt.hrl"). - -%% emqx_gen_mod callbacks --export([ load/1 - , unload/1 - , description/0 - ]). - -%% APIs --export([on_client_connected/3]). - -%%-------------------------------------------------------------------- -%% Load/Unload Hook -%%-------------------------------------------------------------------- - -load(Topics) -> - emqx_hooks:add('client.connected', {?MODULE, on_client_connected, [Topics]}). - -on_client_connected(#{clientid := ClientId, username := Username}, _ConnInfo = #{proto_ver := ProtoVer}, Topics) -> - Replace = fun(Topic) -> - rep(<<"%u">>, Username, rep(<<"%c">>, ClientId, Topic)) - end, - TopicFilters = case ProtoVer of - ?MQTT_PROTO_V5 -> [{Replace(Topic), SubOpts} || {Topic, SubOpts} <- Topics]; - _ -> [{Replace(Topic), #{qos => Qos}} || {Topic, #{qos := Qos}} <- Topics] - end, - self() ! {subscribe, TopicFilters}. - -unload(_) -> - emqx_hooks:del('client.connected', {?MODULE, on_client_connected}). - -description() -> - "EMQ X Subscription Module". -%%-------------------------------------------------------------------- -%% Internal functions -%%-------------------------------------------------------------------- - -rep(<<"%c">>, ClientId, Topic) -> - emqx_topic:feed_var(<<"%c">>, ClientId, Topic); -rep(<<"%u">>, undefined, Topic) -> - Topic; -rep(<<"%u">>, Username, Topic) -> - emqx_topic:feed_var(<<"%u">>, Username, Topic). - diff --git a/src/emqx_mod_sup.erl b/src/emqx_mod_sup.erl deleted file mode 100644 index b5512013c..000000000 --- a/src/emqx_mod_sup.erl +++ /dev/null @@ -1,63 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_mod_sup). - --behaviour(supervisor). - --include("types.hrl"). - --export([ start_link/0 - , start_child/1 - , start_child/2 - , stop_child/1 - ]). - --export([init/1]). - -%% Helper macro for declaring children of supervisor --define(CHILD(Mod, Type), #{id => Mod, - start => {Mod, start_link, []}, - restart => permanent, - shutdown => 5000, - type => Type, - modules => [Mod]}). - --spec(start_link() -> startlink_ret()). -start_link() -> - supervisor:start_link({local, ?MODULE}, ?MODULE, []). - -start_child(ChildSpec) when is_map(ChildSpec) -> - supervisor:start_child(?MODULE, ChildSpec). - -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, term()}). -stop_child(ChildId) -> - case supervisor:terminate_child(?MODULE, ChildId) of - ok -> supervisor:delete_child(?MODULE, ChildId); - Error -> Error - end. - -%%-------------------------------------------------------------------- -%% Supervisor callbacks -%%-------------------------------------------------------------------- - -init([]) -> - ok = emqx_tables:new(emqx_modules, [set, public, {write_concurrency, true}]), - {ok, {{one_for_one, 10, 100}, []}}. - diff --git a/src/emqx_mod_topic_metrics.erl b/src/emqx_mod_topic_metrics.erl deleted file mode 100644 index 813d8d059..000000000 --- a/src/emqx_mod_topic_metrics.erl +++ /dev/null @@ -1,382 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_mod_topic_metrics). - --behaviour(gen_server). --behaviour(emqx_gen_mod). - --include("emqx.hrl"). --include("logger.hrl"). --include("emqx_mqtt.hrl"). - --logger_header("[TOPIC_METRICS]"). - --export([ load/1 - , unload/1 - , description/0 - ]). - --export([ on_message_publish/1 - , on_message_delivered/2 - , on_message_dropped/3 - ]). - -%% API functions --export([ start_link/0 - , stop/0 - ]). - --export([ inc/2 - , inc/3 - , val/2 - , rate/2 - , metrics/1 - , register/1 - , unregister/1 - , unregister_all/0 - , is_registered/1 - , all_registered_topics/0 - ]). - -%% gen_server callbacks --export([ init/1 - , handle_call/3 - , handle_info/2 - , handle_cast/2 - , terminate/2 - ]). - --define(CRefID(Topic), {?MODULE, Topic}). - --define(MAX_TOPICS, 512). --define(TAB, ?MODULE). - --define(TOPIC_METRICS, - ['messages.in', - 'messages.out', - 'messages.qos0.in', - 'messages.qos0.out', - 'messages.qos1.in', - 'messages.qos1.out', - 'messages.qos2.in', - 'messages.qos2.out', - 'messages.dropped' - ]). - --define(TICKING_INTERVAL, 1). - --record(speed, { - last = 0 :: number(), - tick = 1 :: number(), - last_v = 0 :: number(), - acc = 0 :: number(), - samples = [] :: list() - }). - --record(state, { - speeds :: #{{binary(), atom()} => #speed{}} - }). - -%%------------------------------------------------------------------------------ -%% APIs -%%------------------------------------------------------------------------------ - -load(_Env) -> - emqx_mod_sup:start_child(?MODULE, worker), - emqx:hook('message.publish', {?MODULE, on_message_publish, []}), - emqx:hook('message.dropped', {?MODULE, on_message_dropped, []}), - emqx:hook('message.delivered', {?MODULE, on_message_delivered, []}). - -unload(_Env) -> - emqx:unhook('message.publish', {?MODULE, on_message_publish}), - emqx:unhook('message.dropped', {?MODULE, on_message_dropped}), - emqx:unhook('message.delivered', {?MODULE, on_message_delivered}), - emqx_mod_sup:stop_child(?MODULE). - -description() -> - "EMQ X Topic Metrics Module". - -on_message_publish(#message{topic = Topic, qos = QoS}) -> - case is_registered(Topic) of - true -> - inc(Topic, 'messages.in'), - case QoS of - ?QOS_0 -> inc(Topic, 'messages.qos0.in'); - ?QOS_1 -> inc(Topic, 'messages.qos1.in'); - ?QOS_2 -> inc(Topic, 'messages.qos2.in') - end; - false -> - ok - end. - -on_message_delivered(_, #message{topic = Topic, qos = QoS}) -> - case is_registered(Topic) of - true -> - inc(Topic, 'messages.out'), - case QoS of - ?QOS_0 -> inc(Topic, 'messages.qos0.out'); - ?QOS_1 -> inc(Topic, 'messages.qos1.out'); - ?QOS_2 -> inc(Topic, 'messages.qos2.out') - end; - false -> - ok - end. - -on_message_dropped(#message{topic = Topic}, _, _) -> - case is_registered(Topic) of - true -> - inc(Topic, 'messages.dropped'); - false -> - ok - end. - -start_link() -> - gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). - -stop() -> - gen_server:stop(?MODULE). - -inc(Topic, Metric) -> - inc(Topic, Metric, 1). - -inc(Topic, Metric, Val) -> - case get_counters(Topic) of - {error, topic_not_found} -> - {error, topic_not_found}; - CRef -> - case metric_idx(Metric) of - {error, invalid_metric} -> - {error, invalid_metric}; - Idx -> - counters:add(CRef, Idx, Val) - end - end. - -val(Topic, Metric) -> - case ets:lookup(?TAB, Topic) of - [] -> - {error, topic_not_found}; - [{Topic, CRef}] -> - case metric_idx(Metric) of - {error, invalid_metric} -> - {error, invalid_metric}; - Idx -> - counters:get(CRef, Idx) - end - end. - -rate(Topic, Metric) -> - gen_server:call(?MODULE, {get_rate, Topic, Metric}). - -metrics(Topic) -> - case ets:lookup(?TAB, Topic) of - [] -> - {error, topic_not_found}; - [{Topic, CRef}] -> - lists:foldl(fun(Metric, Acc) -> - [{to_count(Metric), counters:get(CRef, metric_idx(Metric))}, - {to_rate(Metric), rate(Topic, Metric)} | Acc] - end, [], ?TOPIC_METRICS) - end. - -register(Topic) when is_binary(Topic) -> - gen_server:call(?MODULE, {register, Topic}). - -unregister(Topic) when is_binary(Topic) -> - gen_server:call(?MODULE, {unregister, Topic}). - -unregister_all() -> - gen_server:call(?MODULE, {unregister, all}). - -is_registered(Topic) -> - ets:member(?TAB, Topic). - -all_registered_topics() -> - [Topic || {Topic, _CRef} <- ets:tab2list(?TAB)]. - -%%-------------------------------------------------------------------- -%% gen_server callbacks -%%-------------------------------------------------------------------- - -init([]) -> - erlang:process_flag(trap_exit, true), - ok = emqx_tables:new(?TAB, [{read_concurrency, true}]), - erlang:send_after(timer:seconds(?TICKING_INTERVAL), self(), ticking), - {ok, #state{speeds = #{}}, hibernate}. - -handle_call({register, Topic}, _From, State = #state{speeds = Speeds}) -> - case is_registered(Topic) of - true -> - {reply, {error, already_existed}, State}; - false -> - case number_of_registered_topics() < ?MAX_TOPICS of - true -> - CRef = counters:new(counters_size(), [write_concurrency]), - true = ets:insert(?TAB, {Topic, CRef}), - [counters:put(CRef, Idx, 0) || Idx <- lists:seq(1, counters_size())], - NSpeeds = lists:foldl(fun(Metric, Acc) -> - maps:put({Topic, Metric}, #speed{}, Acc) - end, Speeds, ?TOPIC_METRICS), - {reply, ok, State#state{speeds = NSpeeds}}; - false -> - {reply, {error, quota_exceeded}, State} - end - end; - -handle_call({unregister, all}, _From, State) -> - [delete_counters(Topic) || {Topic, _CRef} <- ets:tab2list(?TAB)], - {reply, ok, State#state{speeds = #{}}}; - -handle_call({unregister, Topic}, _From, State = #state{speeds = Speeds}) -> - case is_registered(Topic) of - false -> - {reply, ok, State}; - true -> - ok = delete_counters(Topic), - NSpeeds = lists:foldl(fun(Metric, Acc) -> - maps:remove({Topic, Metric}, Acc) - end, Speeds, ?TOPIC_METRICS), - {reply, ok, State#state{speeds = NSpeeds}} - end; - -handle_call({get_rate, Topic, Metric}, _From, State = #state{speeds = Speeds}) -> - case is_registered(Topic) of - false -> - {reply, {error, topic_not_found}, State}; - true -> - case maps:get({Topic, Metric}, Speeds, undefined) of - undefined -> - {reply, {error, invalid_metric}, State}; - #speed{last = Last} -> - {reply, Last, State} - end - end. - -handle_cast(Msg, State) -> - ?LOG(error, "Unexpected cast: ~p", [Msg]), - {noreply, State}. - -handle_info(ticking, State = #state{speeds = Speeds}) -> - NSpeeds = maps:map( - fun({Topic, Metric}, Speed) -> - case val(Topic, Metric) of - {error, topic_not_found} -> maps:remove({Topic, Metric}, Speeds); - Val -> calculate_speed(Val, Speed) - end - end, Speeds), - erlang:send_after(timer:seconds(5), self(), ticking), - {noreply, State#state{speeds = NSpeeds}}; - -handle_info(Info, State) -> - ?LOG(error, "Unexpected info: ~p", [Info]), - {noreply, State}. - -terminate(_Reason, _State) -> - ok. - -%%------------------------------------------------------------------------------ -%% Internal Functions -%%------------------------------------------------------------------------------ - -metric_idx('messages.in') -> 01; -metric_idx('messages.out') -> 02; -metric_idx('messages.qos0.in') -> 03; -metric_idx('messages.qos0.out') -> 04; -metric_idx('messages.qos1.in') -> 05; -metric_idx('messages.qos1.out') -> 06; -metric_idx('messages.qos2.in') -> 07; -metric_idx('messages.qos2.out') -> 08; -metric_idx('messages.dropped') -> 09; -metric_idx(_) -> - {error, invalid_metric}. - -to_count('messages.in') -> - 'messages.in.count'; -to_count('messages.out') -> - 'messages.out.count'; -to_count('messages.qos0.in') -> - 'messages.qos0.in.count'; -to_count('messages.qos0.out') -> - 'messages.qos0.out.count'; -to_count('messages.qos1.in') -> - 'messages.qos1.in.count'; -to_count('messages.qos1.out') -> - 'messages.qos1.out.count'; -to_count('messages.qos2.in') -> - 'messages.qos2.in.count'; -to_count('messages.qos2.out') -> - 'messages.qos2.out.count'; -to_count('messages.dropped') -> - 'messages.dropped.count'. - -to_rate('messages.in') -> - 'messages.in.rate'; -to_rate('messages.out') -> - 'messages.out.rate'; -to_rate('messages.qos0.in') -> - 'messages.qos0.in.rate'; -to_rate('messages.qos0.out') -> - 'messages.qos0.out.rate'; -to_rate('messages.qos1.in') -> - 'messages.qos1.in.rate'; -to_rate('messages.qos1.out') -> - 'messages.qos1.out.rate'; -to_rate('messages.qos2.in') -> - 'messages.qos2.in.rate'; -to_rate('messages.qos2.out') -> - 'messages.qos2.out.rate'; -to_rate('messages.dropped') -> - 'messages.dropped.rate'. - -delete_counters(Topic) -> - true = ets:delete(?TAB, Topic), - ok. - -get_counters(Topic) -> - case ets:lookup(?TAB, Topic) of - [] -> {error, topic_not_found}; - [{Topic, CRef}] -> CRef - end. - -counters_size() -> - length(?TOPIC_METRICS). - -number_of_registered_topics() -> - proplists:get_value(size, ets:info(?TAB)). - -calculate_speed(CurVal, #speed{last_v = LastVal, tick = Tick, acc = Acc, samples = Samples}) -> - %% calculate the current speed based on the last value of the counter - CurSpeed = (CurVal - LastVal) / ?TICKING_INTERVAL, - - %% calculate the average speed in last 5 seconds - case Tick =< 5 of - true -> - Acc1 = Acc + CurSpeed, - #speed{last = Acc1 / Tick, - last_v = CurVal, - acc = Acc1, - samples = Samples ++ [CurSpeed], - tick = Tick + 1}; - false -> - [FirstSpeed | Speeds] = Samples, - Acc1 = Acc + CurSpeed - FirstSpeed, - #speed{last = Acc1 / Tick, - last_v = CurVal, - acc = Acc1, - samples = Speeds ++ [CurSpeed], - tick = Tick} - end. diff --git a/src/emqx_modules.erl b/src/emqx_modules.erl deleted file mode 100644 index 55fb44001..000000000 --- a/src/emqx_modules.erl +++ /dev/null @@ -1,169 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_modules). - --include("logger.hrl"). - --logger_header("[Modules]"). - --export([ list/0 - , load/0 - , load/1 - , unload/0 - , unload/1 - , reload/1 - , find_module/1 - , load_module/2 - ]). - -%% @doc List all available plugins --spec(list() -> [{atom(), boolean()}]). -list() -> - ets:tab2list(?MODULE). - -%% @doc Load all the extended modules. --spec(load() -> ok). -load() -> - case emqx:get_env(modules_loaded_file) of - undefined -> ok; - File -> - load_modules(File) - end. - -load(ModuleName) -> - case find_module(ModuleName) of - [] -> - ?LOG(alert, "Module ~s not found, cannot load it", [ModuleName]), - {error, not_found}; - [{ModuleName, true}] -> - ?LOG(notice, "Module ~s is already started", [ModuleName]), - {error, already_started}; - [{ModuleName, false}] -> - emqx_modules:load_module(ModuleName, true) - end. - -%% @doc Unload all the extended modules. --spec(unload() -> ok). -unload() -> - case emqx:get_env(modules_loaded_file) of - undefined -> ignore; - File -> - unload_modules(File) - end. - -unload(ModuleName) -> - case find_module(ModuleName) of - [] -> - ?LOG(alert, "Module ~s not found, cannot load it", [ModuleName]), - {error, not_found}; - [{ModuleName, false}] -> - ?LOG(error, "Module ~s is not started", [ModuleName]), - {error, not_started}; - [{ModuleName, true}] -> - unload_module(ModuleName, true) - end. - -reload(emqx_mod_acl_internal) -> - Modules = emqx:get_env(modules, []), - Env = proplists:get_value(emqx_mod_acl_internal, Modules, undefined), - case emqx_mod_acl_internal:reload(Env) of - ok -> - ?LOG(info, "Reload ~s module successfully.", [emqx_mod_acl_internal]); - {error, Error} -> - ?LOG(error, "Reload module ~s failed, cannot start for ~0p", [emqx_mod_acl_internal, Error]) - end; -reload(_) -> - ignore. - -find_module(ModuleName) -> - ets:lookup(?MODULE, ModuleName). - -filter_module(ModuleNames) -> - filter_module(ModuleNames, emqx:get_env(modules, [])). -filter_module([], Acc) -> - Acc; -filter_module([{ModuleName, true} | ModuleNames], Acc) -> - filter_module(ModuleNames, lists:keydelete(ModuleName, 1, Acc)); -filter_module([{_, false} | ModuleNames], Acc) -> - filter_module(ModuleNames, Acc). - -load_modules(File) -> - case file:consult(File) of - {ok, ModuleNames} -> - lists:foreach(fun({ModuleName, _}) -> - ets:insert(?MODULE, {ModuleName, false}) - end, filter_module(ModuleNames)), - lists:foreach(fun load_module/1, ModuleNames); - {error, Error} -> - ?LOG(alert, "Failed to read: ~p, error: ~p", [File, Error]) - end. - -load_module({ModuleName, true}) -> - emqx_modules:load_module(ModuleName, false); -load_module({ModuleName, false}) -> - ets:insert(?MODULE, {ModuleName, false}); -load_module(ModuleName) -> - load_module({ModuleName, true}). - -load_module(ModuleName, Persistent) -> - Modules = emqx:get_env(modules, []), - Env = proplists:get_value(ModuleName, Modules, undefined), - case ModuleName:load(Env) of - ok -> - ets:insert(?MODULE, {ModuleName, true}), - write_loaded(Persistent), - ?LOG(info, "Load ~s module successfully.", [ModuleName]); - {error, Error} -> - ?LOG(error, "Load module ~s failed, cannot load for ~0p", [ModuleName, Error]), - {error, Error} - end. - -unload_modules(File) -> - case file:consult(File) of - {ok, ModuleNames} -> - lists:foreach(fun unload_module/1, ModuleNames); - {error, Error} -> - ?LOG(alert, "Failed to read: ~p, error: ~p", [File, Error]) - end. -unload_module({ModuleName, true}) -> - unload_module(ModuleName, false); -unload_module({ModuleName, false}) -> - ets:insert(?MODULE, {ModuleName, false}); -unload_module(ModuleName) -> - unload_module({ModuleName, true}). - -unload_module(ModuleName, Persistent) -> - Modules = emqx:get_env(modules, []), - Env = proplists:get_value(ModuleName, Modules, undefined), - case ModuleName:unload(Env) of - ok -> - ets:insert(?MODULE, {ModuleName, false}), - write_loaded(Persistent), - ?LOG(info, "Unload ~s module successfully.", [ModuleName]); - {error, Error} -> - ?LOG(error, "Unload module ~s failed, cannot unload for ~0p", [ModuleName, Error]) - end. - -write_loaded(true) -> - FilePath = emqx:get_env(modules_loaded_file), - case file:write_file(FilePath, [io_lib:format("~p.~n", [Name]) || Name <- list()]) of - ok -> ok; - {error, Error} -> - ?LOG(error, "Write File ~p Error: ~p", [FilePath, Error]), - {error, Error} - end; -write_loaded(false) -> ok. diff --git a/src/emqx_session.erl b/src/emqx_session.erl index 88664e028..e89943f2a 100644 --- a/src/emqx_session.erl +++ b/src/emqx_session.erl @@ -429,7 +429,7 @@ deliver(Delivers, Session) -> deliver([], Publishes, Session) -> {ok, lists:reverse(Publishes), Session}; -deliver([Msg|More], Acc, Session) -> +deliver([Msg | More], Acc, Session) -> case deliver_msg(Msg, Session) of {ok, Session1} -> deliver(More, Acc, Session1); diff --git a/src/emqx_sup.erl b/src/emqx_sup.erl index c0aa3a2a8..1782b64a0 100644 --- a/src/emqx_sup.erl +++ b/src/emqx_sup.erl @@ -67,12 +67,11 @@ init([]) -> BrokerSup = child_spec(emqx_broker_sup, supervisor), CMSup = child_spec(emqx_cm_sup, supervisor), SysSup = child_spec(emqx_sys_sup, supervisor), - ModSup = child_spec(emqx_mod_sup, supervisor), Childs = [KernelSup] ++ [RouterSup || emqx_boot:is_enabled(router)] ++ [BrokerSup || emqx_boot:is_enabled(broker)] ++ [CMSup || emqx_boot:is_enabled(broker)] ++ - [SysSup] ++ [ModSup], + [SysSup], SupFlags = #{strategy => one_for_all, intensity => 0, period => 1 diff --git a/src/emqx_types.erl b/src/emqx_types.erl index 9bc1d2e87..291e35538 100644 --- a/src/emqx_types.erl +++ b/src/emqx_types.erl @@ -121,6 +121,7 @@ conn_props := properties(), connected := boolean(), connected_at := non_neg_integer(), + disconnected_at => non_neg_integer(), keepalive := 0..16#FFFF, receive_maximum := non_neg_integer(), expiry_interval := non_neg_integer(), diff --git a/test/emqx_access_SUITE_data/acl_temp.conf b/test/emqx_access_SUITE_data/acl_temp.conf new file mode 100644 index 000000000..ca1d4381b --- /dev/null +++ b/test/emqx_access_SUITE_data/acl_temp.conf @@ -0,0 +1 @@ +{deny, {client, "batch_test"}, subscribe, ["t1", "t2", "t3"]}. diff --git a/test/emqx_access_rule_SUITE.erl b/test/emqx_access_rule_SUITE.erl deleted file mode 100644 index d99a1fd78..000000000 --- a/test/emqx_access_rule_SUITE.erl +++ /dev/null @@ -1,97 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_access_rule_SUITE). - --compile(export_all). --compile(nowarn_export_all). - --include_lib("eunit/include/eunit.hrl"). - -all() -> emqx_ct:all(?MODULE). - -init_per_suite(Config) -> - emqx_ct_helpers:boot_modules([router, broker]), - emqx_ct_helpers:start_apps([]), - Config. - -end_per_suite(_Config) -> - emqx_ct_helpers:stop_apps([]). - -t_compile(_) -> - Rule1 = {allow, all, pubsub, <<"%u">>}, - Compile1 = {allow, all, pubsub, [{pattern,[<<"%u">>]}]}, - - Rule2 = {allow, {ipaddr, "127.0.0.1"}, pubsub, <<"%c">>}, - Compile2 = {allow, {ipaddr, {{127,0,0,1}, {127,0,0,1}, 32}}, pubsub, [{pattern,[<<"%c">>]}]}, - - Rule3 = {allow, {'and', [{client, <<"testClient">>}, {user, <<"testUser">>}]}, pubsub, [<<"testTopics1">>, <<"testTopics2">>]}, - Compile3 = {allow, {'and', [{client, <<"testClient">>}, {user, <<"testUser">>}]}, pubsub, [[<<"testTopics1">>], [<<"testTopics2">>]]}, - - Rule4 = {allow, {'or', [{client, all}, {user, all}]}, pubsub, [ <<"testTopics1">>, <<"testTopics2">>]}, - Compile4 = {allow, {'or', [{client, all}, {user, all}]}, pubsub, [[<<"testTopics1">>], [<<"testTopics2">>]]}, - - ?assertEqual(Compile1, emqx_access_rule:compile(Rule1)), - ?assertEqual(Compile2, emqx_access_rule:compile(Rule2)), - ?assertEqual(Compile3, emqx_access_rule:compile(Rule3)), - ?assertEqual(Compile4, emqx_access_rule:compile(Rule4)). - -t_match(_) -> - ClientInfo1 = #{zone => external, - clientid => <<"testClient">>, - username => <<"TestUser">>, - peerhost => {127,0,0,1} - }, - ClientInfo2 = #{zone => external, - clientid => <<"testClient">>, - username => <<"TestUser">>, - peerhost => {192,168,0,10} - }, - ClientInfo3 = #{zone => external, - clientid => <<"testClient">>, - username => <<"TestUser">>, - peerhost => undefined - }, - ?assertEqual({matched, deny}, emqx_access_rule:match([], [], {deny, all})), - ?assertEqual({matched, allow}, emqx_access_rule:match([], [], {allow, all})), - ?assertEqual(nomatch, emqx_access_rule:match(ClientInfo1, <<"Test/Topic">>, - emqx_access_rule:compile({allow, {user, all}, pubsub, []}))), - ?assertEqual({matched, allow}, emqx_access_rule:match(ClientInfo1, <<"Test/Topic">>, - emqx_access_rule:compile({allow, {client, all}, pubsub, ["$SYS/#", "#"]}))), - ?assertEqual(nomatch, emqx_access_rule:match(ClientInfo3, <<"Test/Topic">>, - emqx_access_rule:compile({allow, {ipaddr, "127.0.0.1"}, pubsub, ["$SYS/#", "#"]}))), - ?assertEqual({matched, allow}, emqx_access_rule:match(ClientInfo1, <<"Test/Topic">>, - emqx_access_rule:compile({allow, {ipaddr, "127.0.0.1"}, subscribe, ["$SYS/#", "#"]}))), - ?assertEqual({matched, allow}, emqx_access_rule:match(ClientInfo2, <<"Test/Topic">>, - emqx_access_rule:compile({allow, {ipaddr, "192.168.0.1/24"}, subscribe, ["$SYS/#", "#"]}))), - ?assertEqual({matched, allow}, emqx_access_rule:match(ClientInfo1, <<"d/e/f/x">>, - emqx_access_rule:compile({allow, {user, "TestUser"}, subscribe, ["a/b/c", "d/e/f/#"]}))), - ?assertEqual(nomatch, emqx_access_rule:match(ClientInfo1, <<"d/e/f/x">>, - emqx_access_rule:compile({allow, {user, "admin"}, pubsub, ["d/e/f/#"]}))), - ?assertEqual({matched, allow}, emqx_access_rule:match(ClientInfo1, <<"testTopics/testClient">>, - emqx_access_rule:compile({allow, {client, "testClient"}, publish, ["testTopics/testClient"]}))), - ?assertEqual({matched, allow}, emqx_access_rule:match(ClientInfo1, <<"clients/testClient">>, - emqx_access_rule:compile({allow, all, pubsub, ["clients/%c"]}))), - ?assertEqual({matched, allow}, emqx_access_rule:match(#{username => <<"user2">>}, <<"users/user2/abc/def">>, - emqx_access_rule:compile({allow, all, subscribe, ["users/%u/#"]}))), - ?assertEqual({matched, deny}, emqx_access_rule:match(ClientInfo1, <<"d/e/f">>, - emqx_access_rule:compile({deny, all, subscribe, ["$SYS/#", "#"]}))), - ?assertEqual(nomatch, emqx_access_rule:match(ClientInfo1, <<"Topic">>, - emqx_access_rule:compile({allow, {'and', [{ipaddr, "127.0.0.1"}, {user, <<"WrongUser">>}]}, publish, <<"Topic">>}))), - ?assertEqual({matched, allow}, emqx_access_rule:match(ClientInfo1, <<"Topic">>, - emqx_access_rule:compile({allow, {'and', [{ipaddr, "127.0.0.1"}, {user, <<"TestUser">>}]}, publish, <<"Topic">>}))), - ?assertEqual({matched, allow}, emqx_access_rule:match(ClientInfo1, <<"Topic">>, - emqx_access_rule:compile({allow, {'or', [{ipaddr, "127.0.0.1"}, {user, <<"WrongUser">>}]}, publish, ["Topic"]}))). diff --git a/test/emqx_acl_cache_SUITE.erl b/test/emqx_acl_cache_SUITE.erl index 6d314598c..2adbed03c 100644 --- a/test/emqx_acl_cache_SUITE.erl +++ b/test/emqx_acl_cache_SUITE.erl @@ -56,7 +56,7 @@ t_clean_acl_cache(_) -> emqtt:stop(Client). % optimize?? -t_reload_aclfile_and_cleanall(Config) -> +t_reload_aclfile_and_cleanall(_Config) -> RasieMsg = fun() -> Self = self(), #{puback => fun(Msg) -> Self ! {puback, Msg} end, disconnected => fun(_) -> ok end, @@ -78,27 +78,6 @@ t_reload_aclfile_and_cleanall(Config) -> %% Check acl cache list [ClientPid] = emqx_cm:lookup_channels(<<"emqx_c">>), ?assert(length(gen_server:call(ClientPid, list_acl_cache)) > 0), - - %% Update acl file and reload mod_acl_internal - Path = filename:join([testdir(proplists:get_value(data_dir, Config)), "acl2.conf"]), - ok = file:write_file(Path, <<"{deny, all}.">>), - OldPath = emqx:get_env(acl_file), - % application:set_env(emqx, acl_file, Path), - emqx_mod_acl_internal:reload([{acl_file, Path}]), - - ?assert(length(gen_server:call(ClientPid, list_acl_cache)) == 0), - {ok, PktId2} = emqtt:publish(Client, <<"t1">>, <<"{\"x\":1}">>, qos1), - - receive - {puback, #{packet_id := PktId2, reason_code := Rc2}} -> - %% Not authorized - ?assertEqual(16#87, Rc2); - _ -> - ?assert(false) - end, - application:set_env(emqx, acl_file, OldPath), - file:delete(Path), - emqx_mod_acl_internal:reload([{acl_file, OldPath}]), emqtt:stop(Client). %% @private diff --git a/test/emqx_acl_test_mod.erl b/test/emqx_acl_test_mod.erl deleted file mode 100644 index 5d36cce78..000000000 --- a/test/emqx_acl_test_mod.erl +++ /dev/null @@ -1,33 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_acl_test_mod). - -%% ACL callbacks --export([ init/1 - , check_acl/2 - , description/0 - ]). - -init(AclOpts) -> - {ok, AclOpts}. - -check_acl({_User, _PubSub, _Topic}, _State) -> - allow. - -description() -> - "Test ACL Mod". - diff --git a/test/emqx_channel_SUITE.erl b/test/emqx_channel_SUITE.erl index 3f282ba6a..c90c7510c 100644 --- a/test/emqx_channel_SUITE.erl +++ b/test/emqx_channel_SUITE.erl @@ -417,6 +417,14 @@ t_handle_deliver(_) -> {ok, {outgoing, Packets}, _Ch} = emqx_channel:handle_deliver(Delivers, channel()), ?assertEqual([?QOS_1, ?QOS_2], [emqx_packet:qos(Pkt)|| Pkt <- Packets]). +t_handle_deliver_nl(_) -> + ClientInfo = clientinfo(#{clientid => <<"clientid">>}), + Session = session(#{subscriptions => #{<<"t1">> => #{nl => 1}}}), + Channel = channel(#{clientinfo => ClientInfo, session => Session}), + Msg = emqx_message:make(<<"clientid">>, ?QOS_1, <<"t1">>, <<"qos1">>), + NMsg = emqx_message:set_flag(nl, Msg), + {ok, Channel} = emqx_channel:handle_deliver([{deliver, <<"t1">>, NMsg}], Channel). + %%-------------------------------------------------------------------- %% Test cases for handle_out %%-------------------------------------------------------------------- @@ -434,13 +442,6 @@ t_handle_out_publish_1(_) -> {ok, {outgoing, [?PUBLISH_PACKET(?QOS_1, <<"t">>, 1, <<"payload">>)]}, _Chan} = emqx_channel:handle_out(publish, [{1, Msg}], channel()). -t_handle_out_publish_nl(_) -> - ClientInfo = clientinfo(#{clientid => <<"clientid">>}), - Channel = channel(#{clientinfo => ClientInfo}), - Msg = emqx_message:make(<<"clientid">>, ?QOS_1, <<"t1">>, <<"qos1">>), - Pubs = [{1, emqx_message:set_flag(nl, Msg)}], - {ok, {outgoing,[]}, Channel} = emqx_channel:handle_out(publish, Pubs, Channel). - t_handle_out_connack_sucess(_) -> {ok, [{event, connected}, {connack, ?CONNACK_PACKET(?RC_SUCCESS, 0, _)}], Channel} = emqx_channel:handle_out(connack, {?RC_SUCCESS, 0, #{}}, channel()), diff --git a/test/emqx_mod_acl_internal_SUITE.erl b/test/emqx_mod_acl_internal_SUITE.erl deleted file mode 100644 index bd9a0bf5a..000000000 --- a/test/emqx_mod_acl_internal_SUITE.erl +++ /dev/null @@ -1,64 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_mod_acl_internal_SUITE). - --compile(export_all). --compile(nowarn_export_all). - --include("emqx_mqtt.hrl"). --include_lib("eunit/include/eunit.hrl"). - -all() -> emqx_ct:all(?MODULE). - -init_per_suite(Config) -> - emqx_ct_helpers:boot_modules(all), - emqx_ct_helpers:start_apps([]), - Config. - -end_per_suite(_Config) -> - emqx_ct_helpers:stop_apps([]). - -t_load_unload(_) -> - ?assertEqual(ok, emqx_mod_acl_internal:unload([])), - ?assertEqual(ok, emqx_mod_acl_internal:load([])), - ?assertEqual({error,already_exists}, emqx_mod_acl_internal:load([])). - -t_check_acl(_) -> - Rules=#{publish => [{allow,all}], subscribe => [{deny, all}]}, - ?assertEqual({ok, allow}, emqx_mod_acl_internal:check_acl(clientinfo(), publish, <<"t">>, [], Rules)), - ?assertEqual({ok, deny}, emqx_mod_acl_internal:check_acl(clientinfo(), subscribe, <<"t">>, [], Rules)), - ?assertEqual(ok, emqx_mod_acl_internal:check_acl(clientinfo(), connect, <<"t">>, [], Rules)). - -t_reload_acl(_) -> - ?assertEqual(ok, emqx_mod_acl_internal:reload([])). - -%%-------------------------------------------------------------------- -%% Helper functions -%%-------------------------------------------------------------------- - -clientinfo() -> clientinfo(#{}). -clientinfo(InitProps) -> - maps:merge(#{zone => zone, - protocol => mqtt, - peerhost => {127,0,0,1}, - clientid => <<"clientid">>, - username => <<"username">>, - password => <<"passwd">>, - is_superuser => false, - peercert => undefined, - mountpoint => undefined - }, InitProps). diff --git a/test/emqx_mod_delayed_SUITE.erl b/test/emqx_mod_delayed_SUITE.erl deleted file mode 100644 index 3759e8c4d..000000000 --- a/test/emqx_mod_delayed_SUITE.erl +++ /dev/null @@ -1,78 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_mod_delayed_SUITE). - --import(emqx_mod_delayed, [on_message_publish/1]). - --compile(export_all). --compile(nowarn_export_all). - --record(delayed_message, {key, msg}). - --include_lib("common_test/include/ct.hrl"). --include_lib("eunit/include/eunit.hrl"). --include_lib("emqx/include/emqx.hrl"). - -%%-------------------------------------------------------------------- -%% Setups -%%-------------------------------------------------------------------- - -all() -> - emqx_ct:all(?MODULE). - -init_per_suite(Config) -> - emqx_ct_helpers:start_apps([], fun set_special_configs/1), - Config. - -end_per_suite(_) -> - emqx_ct_helpers:stop_apps([]). - -set_special_configs(emqx) -> - application:set_env(emqx, modules, [{emqx_mod_delayed, []}]), - application:set_env(emqx, allow_anonymous, false), - application:set_env(emqx, enable_acl_cache, false); -set_special_configs(_App) -> - ok. - -%%-------------------------------------------------------------------- -%% Test cases -%%-------------------------------------------------------------------- - -t_load_case(_) -> - UnHooks = emqx_hooks:lookup('message.publish'), - ?assertEqual([], UnHooks), - ok = emqx_mod_delayed:load([]), - Hooks = emqx_hooks:lookup('message.publish'), - ?assertEqual(1, length(Hooks)), - ok. - -t_delayed_message(_) -> - ok = emqx_mod_delayed:load([]), - DelayedMsg = emqx_message:make(?MODULE, 1, <<"$delayed/1/publish">>, <<"delayed_m">>), - ?assertEqual({stop, DelayedMsg#message{topic = <<"publish">>, headers = #{allow_publish => false}}}, on_message_publish(DelayedMsg)), - - Msg = emqx_message:make(?MODULE, 1, <<"no_delayed_msg">>, <<"no_delayed">>), - ?assertEqual({ok, Msg}, on_message_publish(Msg)), - - [Key] = mnesia:dirty_all_keys(emqx_mod_delayed), - [#delayed_message{msg = #message{payload = Payload}}] = mnesia:dirty_read({emqx_mod_delayed, Key}), - ?assertEqual(<<"delayed_m">>, Payload), - timer:sleep(5000), - - EmptyKey = mnesia:dirty_all_keys(emqx_mod_delayed), - ?assertEqual([], EmptyKey), - ok = emqx_mod_delayed:unload([]). diff --git a/test/emqx_mod_presence_SUITE.erl b/test/emqx_mod_presence_SUITE.erl deleted file mode 100644 index 49d5d206b..000000000 --- a/test/emqx_mod_presence_SUITE.erl +++ /dev/null @@ -1,88 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_mod_presence_SUITE). - --compile(export_all). --compile(nowarn_export_all). - --include("emqx_mqtt.hrl"). --include_lib("eunit/include/eunit.hrl"). - -all() -> emqx_ct:all(?MODULE). - -init_per_suite(Config) -> - emqx_ct_helpers:boot_modules(all), - emqx_ct_helpers:start_apps([]), - %% Ensure all the modules unloaded. - ok = emqx_modules:unload(), - Config. - -end_per_suite(_Config) -> - emqx_ct_helpers:stop_apps([]). - -%% Test case for emqx_mod_presence -t_mod_presence(_) -> - ok = emqx_mod_presence:load([{qos, ?QOS_1}]), - {ok, C1} = emqtt:start_link([{clientid, <<"monsys">>}]), - {ok, _} = emqtt:connect(C1), - {ok, _Props, [?QOS_1]} = emqtt:subscribe(C1, <<"$SYS/brokers/+/clients/#">>, qos1), - %% Connected Presence - {ok, C2} = emqtt:start_link([{clientid, <<"clientid">>}, - {username, <<"username">>}]), - {ok, _} = emqtt:connect(C2), - ok = recv_and_check_presence(<<"clientid">>, <<"connected">>), - %% Disconnected Presence - ok = emqtt:disconnect(C2), - ok = recv_and_check_presence(<<"clientid">>, <<"disconnected">>), - ok = emqtt:disconnect(C1), - ok = emqx_mod_presence:unload([{qos, ?QOS_1}]). - -t_mod_presence_reason(_) -> - ?assertEqual(normal, emqx_mod_presence:reason(normal)), - ?assertEqual(discarded, emqx_mod_presence:reason({shutdown, discarded})), - ?assertEqual(tcp_error, emqx_mod_presence:reason({tcp_error, einval})), - ?assertEqual(internal_error, emqx_mod_presence:reason(<<"unknown error">>)). - -recv_and_check_presence(ClientId, Presence) -> - {ok, #{qos := ?QOS_1, topic := Topic, payload := Payload}} = receive_publish(100), - ?assertMatch([<<"$SYS">>, <<"brokers">>, _Node, <<"clients">>, ClientId, Presence], - binary:split(Topic, <<"/">>, [global])), - case Presence of - <<"connected">> -> - ?assertMatch(#{<<"clientid">> := <<"clientid">>, - <<"username">> := <<"username">>, - <<"ipaddress">> := <<"127.0.0.1">>, - <<"proto_name">> := <<"MQTT">>, - <<"proto_ver">> := ?MQTT_PROTO_V4, - <<"connack">> := ?RC_SUCCESS, - <<"clean_start">> := true}, emqx_json:decode(Payload, [return_maps])); - <<"disconnected">> -> - ?assertMatch(#{<<"clientid">> := <<"clientid">>, - <<"username">> := <<"username">>, - <<"reason">> := <<"normal">>}, emqx_json:decode(Payload, [return_maps])) - end. - -%%-------------------------------------------------------------------- -%% Internal functions -%%-------------------------------------------------------------------- - -receive_publish(Timeout) -> - receive - {publish, Publish} -> {ok, Publish} - after - Timeout -> {error, timeout} - end. diff --git a/test/emqx_mod_rewrite_SUITE.erl b/test/emqx_mod_rewrite_SUITE.erl deleted file mode 100644 index e3c19200e..000000000 --- a/test/emqx_mod_rewrite_SUITE.erl +++ /dev/null @@ -1,93 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_mod_rewrite_SUITE). - --compile(export_all). --compile(nowarn_export_all). - --include("emqx_mqtt.hrl"). --include_lib("eunit/include/eunit.hrl"). - --define(RULES, [{rewrite, pub, <<"x/#">>,<<"^x/y/(.+)$">>,<<"z/y/$1">>}, - {rewrite, sub, <<"y/+/z/#">>,<<"^y/(.+)/z/(.+)$">>,<<"y/z/$2">>} - ]). - -all() -> emqx_ct:all(?MODULE). - -init_per_suite(Config) -> - emqx_ct_helpers:boot_modules(all), - emqx_ct_helpers:start_apps([]), - %% Ensure all the modules unloaded. - ok = emqx_modules:unload(), - Config. - -end_per_suite(_Config) -> - emqx_ct_helpers:stop_apps([]). - -%% Test case for emqx_mod_write -t_mod_rewrite(_Config) -> - ok = emqx_mod_rewrite:load(?RULES), - {ok, C} = emqtt:start_link([{clientid, <<"rewrite_client">>}]), - {ok, _} = emqtt:connect(C), - PubOrigTopics = [<<"x/y/2">>, <<"x/1/2">>], - PubDestTopics = [<<"z/y/2">>, <<"x/1/2">>], - SubOrigTopics = [<<"y/a/z/b">>, <<"y/def">>], - SubDestTopics = [<<"y/z/b">>, <<"y/def">>], - %% Sub Rules - {ok, _Props, _} = emqtt:subscribe(C, [{Topic, ?QOS_1} || Topic <- SubOrigTopics]), - timer:sleep(100), - Subscriptions = emqx_broker:subscriptions(<<"rewrite_client">>), - ?assertEqual(SubDestTopics, [Topic || {Topic, _SubOpts} <- Subscriptions]), - RecvTopics1 = [begin - ok = emqtt:publish(C, Topic, <<"payload">>), - {ok, #{topic := RecvTopic}} = receive_publish(100), - RecvTopic - end || Topic <- SubDestTopics], - ?assertEqual(SubDestTopics, RecvTopics1), - {ok, _, _} = emqtt:unsubscribe(C, SubOrigTopics), - timer:sleep(100), - ?assertEqual([], emqx_broker:subscriptions(<<"rewrite_client">>)), - %% Pub Rules - {ok, _Props, _} = emqtt:subscribe(C, [{Topic, ?QOS_1} || Topic <- PubDestTopics]), - RecvTopics2 = [begin - ok = emqtt:publish(C, Topic, <<"payload">>), - {ok, #{topic := RecvTopic}} = receive_publish(100), - RecvTopic - end || Topic <- PubOrigTopics], - ?assertEqual(PubDestTopics, RecvTopics2), - {ok, _, _} = emqtt:unsubscribe(C, PubDestTopics), - - ok = emqtt:disconnect(C), - ok = emqx_mod_rewrite:unload(?RULES). - -t_rewrite_rule(_Config) -> - {PubRules, SubRules} = emqx_mod_rewrite:compile(?RULES), - ?assertEqual(<<"z/y/2">>, emqx_mod_rewrite:match_and_rewrite(<<"x/y/2">>, PubRules)), - ?assertEqual(<<"x/1/2">>, emqx_mod_rewrite:match_and_rewrite(<<"x/1/2">>, PubRules)), - ?assertEqual(<<"y/z/b">>, emqx_mod_rewrite:match_and_rewrite(<<"y/a/z/b">>, SubRules)), - ?assertEqual(<<"y/def">>, emqx_mod_rewrite:match_and_rewrite(<<"y/def">>, SubRules)). - -%%-------------------------------------------------------------------- -%% Internal functions -%%-------------------------------------------------------------------- - -receive_publish(Timeout) -> - receive - {publish, Publish} -> {ok, Publish} - after - Timeout -> {error, timeout} - end. diff --git a/test/emqx_mod_subscription_SUITE.erl b/test/emqx_mod_subscription_SUITE.erl deleted file mode 100644 index 06cd17cea..000000000 --- a/test/emqx_mod_subscription_SUITE.erl +++ /dev/null @@ -1,92 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_mod_subscription_SUITE). - --compile(export_all). --compile(nowarn_export_all). - --include("emqx_mqtt.hrl"). --include_lib("eunit/include/eunit.hrl"). - -all() -> emqx_ct:all(?MODULE). - -init_per_suite(Config) -> - emqx_ct_helpers:boot_modules(all), - emqx_ct_helpers:start_apps([]), - Config. - -end_per_suite(_Config) -> - emqx_ct_helpers:stop_apps([]). - -t_on_client_connected(_) -> - ?assertEqual(ok, emqx_mod_subscription:load([{<<"connected/%c/%u">>, #{qos => ?QOS_0}}])), - {ok, C} = emqtt:start_link([{host, "localhost"}, - {clientid, "myclient"}, - {username, "admin"}]), - {ok, _} = emqtt:connect(C), - emqtt:publish(C, <<"connected/myclient/admin">>, <<"Hello world">>, ?QOS_0), - {ok, #{topic := Topic, payload := Payload}} = receive_publish(100), - ?assertEqual(<<"connected/myclient/admin">>, Topic), - ?assertEqual(<<"Hello world">>, Payload), - ok = emqtt:disconnect(C), - ?assertEqual(ok, emqx_mod_subscription:unload([{<<"connected/%c/%u">>, #{qos => ?QOS_0}}])). - -t_on_undefined_client_connected(_) -> - ?assertEqual(ok, emqx_mod_subscription:load([{<<"connected/undefined">>, #{qos => ?QOS_1}}])), - {ok, C} = emqtt:start_link([{host, "localhost"}]), - {ok, _} = emqtt:connect(C), - emqtt:publish(C, <<"connected/undefined">>, <<"Hello world">>, ?QOS_1), - {ok, #{topic := Topic, payload := Payload}} = receive_publish(100), - ?assertEqual(<<"connected/undefined">>, Topic), - ?assertEqual(<<"Hello world">>, Payload), - ok = emqtt:disconnect(C), - ?assertEqual(ok, emqx_mod_subscription:unload([{<<"connected/undefined">>, #{qos => ?QOS_1}}])). - -t_suboption(_) -> - Client_info = fun(Key, Client) -> maps:get(Key, maps:from_list(emqtt:info(Client)), undefined) end, - Suboption = #{qos => ?QOS_2, nl => 1, rap => 1, rh => 2}, - ?assertEqual(ok, emqx_mod_subscription:load([{<<"connected/%c/%u">>, Suboption}])), - {ok, C1} = emqtt:start_link([{proto_ver, v5}]), - {ok, _} = emqtt:connect(C1), - timer:sleep(200), - [CPid1] = emqx_cm:lookup_channels(Client_info(clientid, C1)), - [ Sub1 | _ ] = ets:lookup(emqx_subscription,CPid1), - [ Suboption1 | _ ] = ets:lookup(emqx_suboption,Sub1), - ?assertMatch({Sub1, #{qos := 2, nl := 1, rap := 1, rh := 2, subid := _}}, Suboption1), - ok = emqtt:disconnect(C1), - %% The subscription option is not valid for MQTT V3.1.1 - {ok, C2} = emqtt:start_link([{proto_ver, v4}]), - {ok, _} = emqtt:connect(C2), - timer:sleep(200), - [CPid2] = emqx_cm:lookup_channels(Client_info(clientid, C2)), - [ Sub2 | _ ] = ets:lookup(emqx_subscription,CPid2), - [ Suboption2 | _ ] = ets:lookup(emqx_suboption,Sub2), - ok = emqtt:disconnect(C2), - ?assertMatch({Sub2, #{qos := 2, nl := 0, rap := 0, rh := 0, subid := _}}, Suboption2), - - ?assertEqual(ok, emqx_mod_subscription:unload([{<<"connected/undefined">>, Suboption}])). - -%%-------------------------------------------------------------------- -%% Internal functions -%%-------------------------------------------------------------------- - -receive_publish(Timeout) -> - receive - {publish, Publish} -> {ok, Publish} - after - Timeout -> {error, timeout} - end. diff --git a/test/emqx_mod_sup_SUITE.erl b/test/emqx_mod_sup_SUITE.erl deleted file mode 100644 index cd4c868ee..000000000 --- a/test/emqx_mod_sup_SUITE.erl +++ /dev/null @@ -1,51 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_mod_sup_SUITE). - --compile(export_all). --compile(nowarn_export_all). - --include_lib("eunit/include/eunit.hrl"). - -all() -> emqx_ct:all(?MODULE). - -%%-------------------------------------------------------------------- -%% Test cases -%%-------------------------------------------------------------------- - -t_start(_) -> - {ok, _} = emqx_mod_sup:start_link(), - ?assertEqual([], supervisor:which_children(emqx_mod_sup)). - -t_start_child(_) -> - %% Set the emqx_mod_sup child with emqx_hooks for test - Mod = emqx_hooks, - Spec = #{id => Mod, - start => {Mod, start_link, []}, - restart => permanent, - shutdown => 5000, - type => worker, - modules => [Mod]}, - - {ok, _} = emqx_mod_sup:start_link(), - {ok, _} = emqx_mod_sup:start_child(Mod, worker), - {error, {already_started, _}} = emqx_mod_sup:start_child(Spec), - - ok = emqx_mod_sup:stop_child(Mod), - {error, not_found} = emqx_mod_sup:stop_child(Mod), - ok. - diff --git a/test/emqx_mod_topic_metrics_SUITE.erl b/test/emqx_mod_topic_metrics_SUITE.erl deleted file mode 100644 index 4ac1b00eb..000000000 --- a/test/emqx_mod_topic_metrics_SUITE.erl +++ /dev/null @@ -1,92 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_mod_topic_metrics_SUITE). - --compile(export_all). --compile(nowarn_export_all). - --include_lib("eunit/include/eunit.hrl"). - -all() -> emqx_ct:all(?MODULE). - -init_per_suite(Config) -> - emqx_ct_helpers:boot_modules(all), - emqx_ct_helpers:start_apps([]), - Config. - -end_per_suite(_Config) -> - emqx_ct_helpers:stop_apps([]). - -t_nonexistent_topic_metrics(_) -> - emqx_mod_topic_metrics:load([]), - ?assertEqual({error, topic_not_found}, emqx_mod_topic_metrics:val(<<"a/b/c">>, 'messages.in')), - ?assertEqual({error, topic_not_found}, emqx_mod_topic_metrics:inc(<<"a/b/c">>, 'messages.in')), - ?assertEqual({error, topic_not_found}, emqx_mod_topic_metrics:rate(<<"a/b/c">>, 'messages.in')), - emqx_mod_topic_metrics:register(<<"a/b/c">>), - ?assertEqual(0, emqx_mod_topic_metrics:val(<<"a/b/c">>, 'messages.in')), - ?assertEqual({error, invalid_metric}, emqx_mod_topic_metrics:val(<<"a/b/c">>, 'invalid.metrics')), - ?assertEqual({error, invalid_metric}, emqx_mod_topic_metrics:inc(<<"a/b/c">>, 'invalid.metrics')), - ?assertEqual({error, invalid_metric}, emqx_mod_topic_metrics:rate(<<"a/b/c">>, 'invalid.metrics')), - emqx_mod_topic_metrics:unregister(<<"a/b/c">>), - emqx_mod_topic_metrics:unload([]). - -t_topic_metrics(_) -> - emqx_mod_topic_metrics:load([]), - - ?assertEqual(false, emqx_mod_topic_metrics:is_registered(<<"a/b/c">>)), - ?assertEqual([], emqx_mod_topic_metrics:all_registered_topics()), - emqx_mod_topic_metrics:register(<<"a/b/c">>), - ?assertEqual(true, emqx_mod_topic_metrics:is_registered(<<"a/b/c">>)), - ?assertEqual([<<"a/b/c">>], emqx_mod_topic_metrics:all_registered_topics()), - - ?assertEqual(0, emqx_mod_topic_metrics:val(<<"a/b/c">>, 'messages.in')), - ?assertEqual(ok, emqx_mod_topic_metrics:inc(<<"a/b/c">>, 'messages.in')), - ?assertEqual(1, emqx_mod_topic_metrics:val(<<"a/b/c">>, 'messages.in')), - ?assert(emqx_mod_topic_metrics:rate(<<"a/b/c">>, 'messages.in') =:= 0), - emqx_mod_topic_metrics:unregister(<<"a/b/c">>), - emqx_mod_topic_metrics:unload([]). - -t_hook(_) -> - emqx_mod_topic_metrics:load([]), - emqx_mod_topic_metrics:register(<<"a/b/c">>), - - ?assertEqual(0, emqx_mod_topic_metrics:val(<<"a/b/c">>, 'messages.in')), - ?assertEqual(0, emqx_mod_topic_metrics:val(<<"a/b/c">>, 'messages.qos0.in')), - ?assertEqual(0, emqx_mod_topic_metrics:val(<<"a/b/c">>, 'messages.out')), - ?assertEqual(0, emqx_mod_topic_metrics:val(<<"a/b/c">>, 'messages.qos0.out')), - ?assertEqual(0, emqx_mod_topic_metrics:val(<<"a/b/c">>, 'messages.dropped')), - - {ok, C} = emqtt:start_link([{host, "localhost"}, - {clientid, "myclient"}, - {username, "myuser"}]), - {ok, _} = emqtt:connect(C), - emqtt:publish(C, <<"a/b/c">>, <<"Hello world">>, 0), - ct:sleep(100), - ?assertEqual(1, emqx_mod_topic_metrics:val(<<"a/b/c">>, 'messages.in')), - ?assertEqual(1, emqx_mod_topic_metrics:val(<<"a/b/c">>, 'messages.qos0.in')), - ?assertEqual(1, emqx_mod_topic_metrics:val(<<"a/b/c">>, 'messages.dropped')), - - emqtt:subscribe(C, <<"a/b/c">>), - emqtt:publish(C, <<"a/b/c">>, <<"Hello world">>, 0), - ct:sleep(100), - ?assertEqual(2, emqx_mod_topic_metrics:val(<<"a/b/c">>, 'messages.in')), - ?assertEqual(2, emqx_mod_topic_metrics:val(<<"a/b/c">>, 'messages.qos0.in')), - ?assertEqual(1, emqx_mod_topic_metrics:val(<<"a/b/c">>, 'messages.out')), - ?assertEqual(1, emqx_mod_topic_metrics:val(<<"a/b/c">>, 'messages.qos0.out')), - ?assertEqual(1, emqx_mod_topic_metrics:val(<<"a/b/c">>, 'messages.dropped')), - emqx_mod_topic_metrics:unregister(<<"a/b/c">>), - emqx_mod_topic_metrics:unload([]). \ No newline at end of file diff --git a/test/emqx_modules_SUITE.erl b/test/emqx_modules_SUITE.erl deleted file mode 100644 index 3fe7864c3..000000000 --- a/test/emqx_modules_SUITE.erl +++ /dev/null @@ -1,47 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_modules_SUITE). - --compile(export_all). --compile(nowarn_export_all). - --include_lib("eunit/include/eunit.hrl"). - -all() -> emqx_ct:all(?MODULE). - -init_per_suite(Config) -> - emqx_ct_helpers:start_apps([], fun set_sepecial_cfg/1), - Config. - -set_sepecial_cfg(_) -> - application:set_env(emqx, modules_loaded_file, emqx_ct_helpers:deps_path(emqx, "test/emqx_SUITE_data/loaded_modules")), - ok. - -end_per_suite(_Config) -> - emqx_ct_helpers:stop_apps([]). - -t_load(_) -> - ?assertEqual(ok, emqx_modules:unload()), - ?assertEqual(ok, emqx_modules:load()), - ?assertEqual({error, not_found}, emqx_modules:load(not_existed_module)), - ?assertEqual({error, not_started}, emqx_modules:unload(emqx_mod_rewrite)), - ?assertEqual(ignore, emqx_modules:reload(emqx_mod_rewrite)), - ?assertEqual(ok, emqx_modules:reload(emqx_mod_acl_internal)). - -t_list(_) -> - ?assertMatch([{_, _} | _ ], emqx_modules:list()). - diff --git a/test/mqtt_protocol_v5_SUITE.erl b/test/mqtt_protocol_v5_SUITE.erl index 50fc9c04d..f0a0cc3dc 100644 --- a/test/mqtt_protocol_v5_SUITE.erl +++ b/test/mqtt_protocol_v5_SUITE.erl @@ -179,10 +179,7 @@ t_batch_subscribe(_) -> {ok, Client} = emqtt:start_link([{proto_ver, v5}, {clientid, <<"batch_test">>}]), {ok, _} = emqtt:connect(Client), application:set_env(emqx, enable_acl_cache, false), - TempAcl = emqx_ct_helpers:deps_path(emqx, "test/emqx_access_SUITE_data/acl_temp.conf"), - file:write_file(TempAcl, "{deny, {client, \"batch_test\"}, subscribe, [\"t1\", \"t2\", \"t3\"]}.\n"), - timer:sleep(10), - emqx_mod_acl_internal:reload([{acl_file, TempAcl}]), + application:set_env(emqx, acl_nomatch, deny), {ok, _, [?RC_NOT_AUTHORIZED, ?RC_NOT_AUTHORIZED, ?RC_NOT_AUTHORIZED]} = emqtt:subscribe(Client, [{<<"t1">>, qos1}, @@ -193,8 +190,9 @@ t_batch_subscribe(_) -> ?RC_NO_SUBSCRIPTION_EXISTED]} = emqtt:unsubscribe(Client, [<<"t1">>, <<"t2">>, <<"t3">>]), - file:delete(TempAcl), + application:set_env(emqx, acl_nomatch, allow), emqtt:disconnect(Client). + t_connect_will_retain(_) -> Topic = nth(1, ?TOPICS), From 827c92c430cb05848841602e835c8955b900574b Mon Sep 17 00:00:00 2001 From: Rory Z Date: Sun, 11 Oct 2020 11:13:39 +0800 Subject: [PATCH 02/26] Auto-pull-request-on-2020-10-11 (#3770) * feature(confs): Split emqx.conf * optimize(application): Optimize emqx module startup sequence * feature: remove the emqx_modules related code * feature: ws/wss add more params * chore: update esockd version to fix ssl:getstat/2 for dtls connection * fix: fix session_locking_strategy enum typos * fix(ignore-loop-deliver): fix issue#3738 (#3741) * chore(paths): cherry-pick v4.2.0 commits and patch it * chore(types): Add the disconnected_at field definition in conninfo (#3764) From 9595b4978965e0f1206a361224e912099a6b1921 Mon Sep 17 00:00:00 2001 From: turtleDeng Date: Mon, 12 Oct 2020 20:07:41 +0800 Subject: [PATCH 03/26] chore(vars) update vars --- vars | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/vars b/vars index 359c27c22..914938fb8 100644 --- a/vars +++ b/vars @@ -1,8 +1,8 @@ %% vars here are for test only, not intended for release -{platform_bin_dir, "bin"}. -{platform_data_dir, "data"}. -{platform_etc_dir, "etc"}. -{platform_lib_dir, "lib"}. -{platform_log_dir, "log"}. -{platform_plugins_dir, "plugins"}. +{platform_bin_dir, "../../lib/emqx/bin"}. +{platform_data_dir, "../../lib/emqx/data"}. +{platform_etc_dir, "../../lib/emqx/etc"}. +{platform_lib_dir, "../../lib/emqx/lib"}. +{platform_log_dir, "../../lib/emqx/log"}. +{platform_plugins_dir, "../../lib/emqx/plugins"}. From b883e238bfe638ee286104594c32baddf78420ae Mon Sep 17 00:00:00 2001 From: JianBo He Date: Mon, 12 Oct 2020 17:05:06 +0800 Subject: [PATCH 04/26] fix(ws): take ws_cookie to clientinfo see: https://github.com/emqx/emqx/issues/3747#issuecomment-702268570 --- src/emqx_channel.erl | 13 +++++++++++-- test/emqx_channel_SUITE.erl | 12 ++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/emqx_channel.erl b/src/emqx_channel.erl index 289ed50f0..71b89f153 100644 --- a/src/emqx_channel.erl +++ b/src/emqx_channel.erl @@ -183,8 +183,9 @@ init(ConnInfo = #{peername := {PeerHost, _Port}, is_bridge => false, is_superuser => false }, Options), - #channel{conninfo = ConnInfo, - clientinfo = ClientInfo, + {NClientInfo, NConnInfo} = take_ws_cookie(ClientInfo, ConnInfo), + #channel{conninfo = NConnInfo, + clientinfo = NClientInfo, topic_aliases = #{inbound => #{}, outbound => #{} }, @@ -213,6 +214,14 @@ setting_peercert_infos(Peercert, ClientInfo, Options) -> end, ClientInfo#{username => Username, dn => DN, cn => CN}. +take_ws_cookie(ClientInfo, ConnInfo) -> + case maps:take(ws_cookie, ConnInfo) of + {WsCookie, NConnInfo} -> + {ClientInfo#{ws_cookie => WsCookie}, NConnInfo}; + _ -> + {ClientInfo, ConnInfo} + end. + %%-------------------------------------------------------------------- %% Handle incoming packet %%-------------------------------------------------------------------- diff --git a/test/emqx_channel_SUITE.erl b/test/emqx_channel_SUITE.erl index c90c7510c..613da40ac 100644 --- a/test/emqx_channel_SUITE.erl +++ b/test/emqx_channel_SUITE.erl @@ -687,6 +687,18 @@ t_terminate(_) -> ok = emqx_channel:terminate(sock_error, channel(#{conn_state => connected})), ok = emqx_channel:terminate({shutdown, kicked}, channel(#{conn_state => connected})). +t_ws_cookie_init(_) -> + WsCookie = [{<<"session_id">>, <<"xyz">>}], + ConnInfo = #{socktype => ws, + peername => {{127,0,0,1}, 3456}, + sockname => {{127,0,0,1}, 1883}, + peercert => nossl, + conn_mod => emqx_ws_connection, + ws_cookie => WsCookie + }, + Channel = emqx_channel:init(ConnInfo, [{zone, zone}]), + ?assertMatch(#{ws_cookie := WsCookie}, emqx_channel:info(clientinfo, Channel)). + %%-------------------------------------------------------------------- %% Helper functions %%-------------------------------------------------------------------- From 139306b61cb22e5bdd411173fb6696b57eefcbe9 Mon Sep 17 00:00:00 2001 From: JianBo He Date: Sat, 24 Oct 2020 11:26:56 +0800 Subject: [PATCH 05/26] fix(json): fix decoding/encoding empty object error --- src/emqx_json.erl | 5 ++++- test/emqx_json_SUITE.erl | 36 ++++++++++++++++++++++++++---------- 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/src/emqx_json.erl b/src/emqx_json.erl index f7caaf727..e79cf09d3 100644 --- a/src/emqx_json.erl +++ b/src/emqx_json.erl @@ -103,6 +103,8 @@ safe_decode(Json, Opts) -> , from_ejson/1 ]}). +to_ejson([{}]) -> + {[]}; to_ejson([{_, _}|_] = L) -> {[{K, to_ejson(V)} || {K, V} <- L ]}; to_ejson(L) when is_list(L) -> @@ -111,6 +113,8 @@ to_ejson(T) -> T. from_ejson(L) when is_list(L) -> [from_ejson(E) || E <- L]; +from_ejson({[]}) -> + [{}]; from_ejson({L}) -> [{Name, from_ejson(Value)} || {Name, Value} <- L]; from_ejson(T) -> T. @@ -118,4 +122,3 @@ from_ejson(T) -> T. to_binary(B) when is_binary(B) -> B; to_binary(L) when is_list(L) -> iolist_to_binary(L). - diff --git a/test/emqx_json_SUITE.erl b/test/emqx_json_SUITE.erl index 67f8108c5..fa544ffbb 100644 --- a/test/emqx_json_SUITE.erl +++ b/test/emqx_json_SUITE.erl @@ -27,6 +27,7 @@ , decode/2 ]). +%% copied jiffy/readme %%-------------------------------------------------------------------- %% Erlang JSON Erlang %% ------------------------------------------------------------------- @@ -45,10 +46,27 @@ %% {[{foo, bar}]} -> {"foo": "bar"} -> {[{<<"foo">>, <<"bar">>}]} %% {[{<<"foo">>, <<"bar">>}]} -> {"foo": "bar"} -> {[{<<"foo">>, <<"bar">>}]} %% #{<<"foo">> => <<"bar">>} -> {"foo": "bar"} -> #{<<"foo">> => <<"bar">>} +%%-------------------------------------------------------------------- + +%% but in emqx_json, we use the jsx style for it: +%%-------------------------------------------------------------------- +%% Erlang JSON Erlang +%% ------------------------------------------------------------------- %% -%% Extension: -%% [{<<"foo">>, <<"bar">>}] -> {"foo": "bar"} -> [{<<"foo">>, <<"bar">>}] -%% +%% null -> null -> null +%% true -> true -> true +%% false -> false -> false +%% "hi" -> [104, 105] -> [104, 105] +%% <<"hi">> -> "hi" -> <<"hi">> +%% hi -> "hi" -> <<"hi">> +%% 1 -> 1 -> 1 +%% 1.25 -> 1.25 -> 1.25 +%% [] -> [] -> [] +%% [true, 1.0] -> [true, 1.0] -> [true, 1.0] +%m [{}] -> {} -> [{}] +%a [{<<"foo">>, <<"bar">>}] -> {"foo": "bar"} -> [{<<"foo">>, <<"bar">>}] +%% #{<<"foo">> => <<"bar">>} -> {"foo": "bar"} -> #{<<"foo">> => <<"bar">>} +%m #{<<"foo">> => [{}]} NOT SUPPORT %%-------------------------------------------------------------------- all() -> emqx_ct:all(?MODULE). @@ -63,9 +81,8 @@ t_decode_encode(_) -> 1.25 = decode(encode(1.25)), [] = decode(encode([])), [true, 1] = decode(encode([true, 1])), - [] = decode(encode({[]})), - [{<<"foo">>, <<"bar">>}] = decode(encode({[{foo, bar}]})), - [{<<"foo">>, <<"bar">>}] = decode(encode({[{<<"foo">>, <<"bar">>}]})), + [{}] = decode(encode([{}])), + [{<<"foo">>, <<"bar">>}] = decode(encode([{foo, bar}])), [{<<"foo">>, <<"bar">>}] = decode(encode([{<<"foo">>, <<"bar">>}])), [[{<<"foo">>, <<"bar">>}]] = decode(encode([[{<<"foo">>, <<"bar">>}]])), [[{<<"foo">>, <<"bar">>}, @@ -92,10 +109,10 @@ t_safe_decode_encode(_) -> 1.25 = safe_encode_decode(1.25), [] = safe_encode_decode([]), [true, 1] = safe_encode_decode([true, 1]), - [] = safe_encode_decode({[]}), - [{<<"foo">>, <<"bar">>}] = safe_encode_decode({[{foo, bar}]}), - [{<<"foo">>, <<"bar">>}] = safe_encode_decode({[{<<"foo">>, <<"bar">>}]}), + [{}] = decode(encode([{}])), + [{<<"foo">>, <<"bar">>}] = safe_encode_decode([{foo, bar}]), [{<<"foo">>, <<"bar">>}] = safe_encode_decode([{<<"foo">>, <<"bar">>}]), + [[{<<"foo">>, <<"bar">>}]] = safe_encode_decode([[{<<"foo">>, <<"bar">>}]]), {ok, Json} = emqx_json:safe_encode(#{<<"foo">> => <<"bar">>}), {ok, #{<<"foo">> := <<"bar">>}} = emqx_json:safe_decode(Json, [return_maps]). @@ -105,4 +122,3 @@ safe_encode_decode(Term) -> {ok, {NTerm}} -> NTerm; {ok, NTerm} -> NTerm end. - From cb50156194ed771f5fb6d1b3cb2371eedf40b2dc Mon Sep 17 00:00:00 2001 From: JianBo He Date: Sat, 24 Oct 2020 13:49:18 +0800 Subject: [PATCH 06/26] chore(esockd): upgrade esockd to 5.7.4 --- rebar.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rebar.config b/rebar.config index be7d01148..df71f9762 100644 --- a/rebar.config +++ b/rebar.config @@ -6,7 +6,7 @@ [{gproc, {git, "https://github.com/uwiger/gproc", {tag, "0.8.0"}}}, {jiffy, {git, "https://github.com/emqx/jiffy", {tag, "1.0.5"}}}, {cowboy, {git, "https://github.com/emqx/cowboy", {tag, "2.7.1"}}}, - {esockd, {git, "https://github.com/emqx/esockd", {tag, "5.7.3"}}}, + {esockd, {git, "https://github.com/emqx/esockd", {tag, "5.7.4"}}}, {ekka, {git, "https://github.com/emqx/ekka", {tag, "0.7.4"}}}, {gen_rpc, {git, "https://github.com/emqx/gen_rpc", {tag, "2.5.0"}}}, {cuttlefish, {git, "https://github.com/emqx/cuttlefish", {tag, "v3.0.0"}}} From cc69225b6d5ae30e60f6ff401c837595113db975 Mon Sep 17 00:00:00 2001 From: Shawn <506895667@qq.com> Date: Tue, 10 Nov 2020 19:07:02 +0800 Subject: [PATCH 07/26] feature(tcp): alarm when tcp connection congested (#3818) --- etc/listeners.conf | 7 ++++++ priv/emqx.schema | 11 +++++++++ src/emqx.appup.src | 14 ++++++++++++ src/emqx_alarm.erl | 2 ++ src/emqx_connection.erl | 41 +++++++++++++++++++++++++++++++--- test/emqx_connection_SUITE.erl | 5 +++++ 6 files changed, 77 insertions(+), 3 deletions(-) create mode 100644 src/emqx.appup.src diff --git a/etc/listeners.conf b/etc/listeners.conf index 2f2f1b5a1..c7d11967d 100644 --- a/etc/listeners.conf +++ b/etc/listeners.conf @@ -116,6 +116,13 @@ listener.tcp.external.send_timeout_close = on ## Value: on | off ## listener.tcp.external.tune_buffer = off +## The socket is set to a busy state when the amount of data queued internally +## by the ERTS socket implementation reaches this limit. +## +## Value: on | off +## Defaults to 1MB +## listener.tcp.external.high_watermark = 1MB + ## The TCP_NODELAY flag for MQTT connections. Small amounts of data are ## sent immediately if the option is enabled. ## diff --git a/priv/emqx.schema b/priv/emqx.schema index 6f4e7012e..21047aaaa 100644 --- a/priv/emqx.schema +++ b/priv/emqx.schema @@ -1238,6 +1238,11 @@ end}. hidden ]}. +{mapping, "listener.tcp.$name.high_watermark", "emqx.listeners", [ + {datatype, bytesize}, + {default, "1MB"} +]}. + {mapping, "listener.tcp.$name.tune_buffer", "emqx.listeners", [ {datatype, flag}, hidden @@ -1330,6 +1335,11 @@ end}. hidden ]}. +{mapping, "listener.ssl.$name.high_watermark", "emqx.listeners", [ + {datatype, bytesize}, + {default, "1MB"} +]}. + {mapping, "listener.ssl.$name.tune_buffer", "emqx.listeners", [ {datatype, flag}, hidden @@ -1840,6 +1850,7 @@ end}. {recbuf, cuttlefish:conf_get(Prefix ++ ".recbuf", Conf, undefined)}, {sndbuf, cuttlefish:conf_get(Prefix ++ ".sndbuf", Conf, undefined)}, {buffer, cuttlefish:conf_get(Prefix ++ ".buffer", Conf, undefined)}, + {high_watermark, cuttlefish:conf_get(Prefix ++ ".high_watermark", Conf, undefined)}, {nodelay, cuttlefish:conf_get(Prefix ++ ".nodelay", Conf, true)}, {reuseaddr, cuttlefish:conf_get(Prefix ++ ".reuseaddr", Conf, undefined)}]) end, diff --git a/src/emqx.appup.src b/src/emqx.appup.src new file mode 100644 index 000000000..cdf2ac0c1 --- /dev/null +++ b/src/emqx.appup.src @@ -0,0 +1,14 @@ +{"4.2.1", + [ + {"4.2.0", [ + {load_module, emqx_alarm, brutal_purge, soft_purge, []}, + {load_module, emqx_connection, brutal_purge, soft_purge, []} + ]} + ], + [ + {"4.2.0", [ + {load_module, emqx_alarm, brutal_purge, soft_purge, []}, + {load_module, emqx_connection, brutal_purge, soft_purge, []} + ]} + ] +}. diff --git a/src/emqx_alarm.erl b/src/emqx_alarm.erl index 37d6fe1e8..ec2cb4338 100644 --- a/src/emqx_alarm.erl +++ b/src/emqx_alarm.erl @@ -344,6 +344,8 @@ normalize_message(partition, #{occurred := Node}) -> list_to_binary(io_lib:format("Partition occurs at node ~s", [Node])); normalize_message(<<"resource", _/binary>>, #{type := Type, id := ID}) -> list_to_binary(io_lib:format("Resource ~s(~s) is down", [Type, ID])); +normalize_message(<<"mqtt_conn/congested/", ClientId/binary>>, _) -> + list_to_binary(io_lib:format("MQTT connection for clientid '~s' is congested", [ClientId])); normalize_message(_Name, _UnknownDetails) -> <<"Unknown alarm">>. diff --git a/src/emqx_connection.erl b/src/emqx_connection.erl index 2ab7f6c1e..f8bbd29fe 100644 --- a/src/emqx_connection.erl +++ b/src/emqx_connection.erl @@ -103,6 +103,9 @@ -define(ENABLED(X), (X =/= undefined)). +-define(ALARM_TCP_CONGEST(Channel), + list_to_binary(io_lib:format("mqtt_conn/congested/~s", [emqx_channel:info(clientid, Channel)]))). + -dialyzer({no_match, [info/2]}). -dialyzer({nowarn_function, [ init/4 , init_state/3 @@ -428,6 +431,7 @@ handle_msg(Msg, State) -> terminate(Reason, State = #state{channel = Channel}) -> ?LOG(debug, "Terminated due to ~p", [Reason]), + emqx_alarm:deactivate(?ALARM_TCP_CONGEST(Channel)), emqx_channel:terminate(Reason, Channel), close_socket(State), exit(Reason). @@ -594,11 +598,12 @@ serialize_and_inc_stats_fun(#state{serialize = Serialize}) -> %% Send data -spec(send(iodata(), state()) -> ok). -send(IoData, #state{transport = Transport, socket = Socket}) -> +send(IoData, #state{transport = Transport, socket = Socket, channel = Channel}) -> Oct = iolist_size(IoData), ok = emqx_metrics:inc('bytes.sent', Oct), emqx_pd:inc_counter(outgoing_bytes, Oct), - case Transport:async_send(Socket, IoData) of + maybe_warn_congestion(Socket, Transport, Channel), + case Transport:async_send(Socket, IoData, [nosuspend]) of ok -> ok; Error = {error, _Reason} -> %% Send an inet_reply to postpone handling the error @@ -606,6 +611,36 @@ send(IoData, #state{transport = Transport, socket = Socket}) -> ok end. +maybe_warn_congestion(Socket, Transport, Channel) -> + IsCongestAlarmSet = is_congestion_alarm_set(), + case is_congested(Socket, Transport) of + true when not IsCongestAlarmSet -> + {ok, Stat} = Transport:getstat(Socket, [recv_cnt, recv_oct, send_cnt, send_oct]), + {ok, Opts} = Transport:getopts(Socket, [high_watermark,high_msgq_watermark, sndbuf, recbuf, buffer]), + ok = set_congestion_alarm(), + emqx_alarm:activate(?ALARM_TCP_CONGEST(Channel), maps:from_list(Stat++Opts)); + false when IsCongestAlarmSet -> + ok = clear_congestion_alarm(), + emqx_alarm:deactivate(?ALARM_TCP_CONGEST(Channel)); + _ -> ok + end. + +is_congested(Socket, Transport) -> + case Transport:getstat(Socket, [send_pend]) of + {ok, [{send_pend, N}]} when N > 0 -> true; + _ -> false + end. + +is_congestion_alarm_set() -> + case erlang:get(conn_congested) of + true -> true; + _ -> false + end. +set_congestion_alarm() -> + erlang:put(conn_congested, true), ok. +clear_congestion_alarm() -> + erlang:put(conn_congested, false), ok. + %%-------------------------------------------------------------------- %% Handle Info @@ -621,7 +656,7 @@ handle_info(activate_socket, State = #state{sockstate = OldSst}) -> end; handle_info({sock_error, Reason}, State) -> - ?LOG(debug, "Socket error: ~p", [Reason]), + Reason =/= closed andalso ?LOG(error, "Socket error: ~p", [Reason]), handle_info({sock_closed, Reason}, close_socket(State)); handle_info(Info, State) -> diff --git a/test/emqx_connection_SUITE.erl b/test/emqx_connection_SUITE.erl index b908fe4ab..2538aeecb 100644 --- a/test/emqx_connection_SUITE.erl +++ b/test/emqx_connection_SUITE.erl @@ -52,6 +52,9 @@ init_per_suite(Config) -> ok = meck:expect(emqx_channel, ensure_disconnected, fun(_, Channel) -> Channel end), + ok = meck:expect(emqx_alarm, activate, fun(_, _) -> ok end), + ok = meck:expect(emqx_alarm, deactivate, fun(_) -> ok end), + Config. end_per_suite(_Config) -> @@ -62,6 +65,7 @@ end_per_suite(_Config) -> ok = meck:unload(emqx_pd), ok = meck:unload(emqx_metrics), ok = meck:unload(emqx_hooks), + ok = meck:unload(emqx_alarm), ok. init_per_testcase(_TestCase, Config) -> @@ -77,6 +81,7 @@ init_per_testcase(_TestCase, Config) -> {ok, [{K, 0} || K <- Options]} end), ok = meck:expect(emqx_transport, async_send, fun(_Sock, _Data) -> ok end), + ok = meck:expect(emqx_transport, async_send, fun(_Sock, _Data, _Opts) -> ok end), ok = meck:expect(emqx_transport, fast_close, fun(_Sock) -> ok end), Config. From e100b0a5e96ae303882dc41fd992ce4ef94f7630 Mon Sep 17 00:00:00 2001 From: zhanghongtong Date: Tue, 10 Nov 2020 21:44:39 +0800 Subject: [PATCH 08/26] feat(emqx_metrics): add new functions to count message received --- src/emqx.appup.src | 6 ++++-- src/emqx_metrics.erl | 13 ++++++++++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/emqx.appup.src b/src/emqx.appup.src index cdf2ac0c1..23a383d77 100644 --- a/src/emqx.appup.src +++ b/src/emqx.appup.src @@ -2,13 +2,15 @@ [ {"4.2.0", [ {load_module, emqx_alarm, brutal_purge, soft_purge, []}, - {load_module, emqx_connection, brutal_purge, soft_purge, []} + {load_module, emqx_connection, brutal_purge, soft_purge, []}, + {load_module, emqx_metrics, brutal_purge, soft_purge, []} ]} ], [ {"4.2.0", [ {load_module, emqx_alarm, brutal_purge, soft_purge, []}, - {load_module, emqx_connection, brutal_purge, soft_purge, []} + {load_module, emqx_connection, brutal_purge, soft_purge, []}, + {load_module, emqx_metrics, brutal_purge, soft_purge, []} ]} ] }. diff --git a/src/emqx_metrics.erl b/src/emqx_metrics.erl index 2a5a82749..4bd529a2e 100644 --- a/src/emqx_metrics.erl +++ b/src/emqx_metrics.erl @@ -21,6 +21,7 @@ -include("logger.hrl"). -include("types.hrl"). -include("emqx_mqtt.hrl"). +-include("emqx.hrl"). -logger_header("[Metrics]"). @@ -49,7 +50,8 @@ ]). %% Inc received/sent metrics --export([ inc_recv/1 +-export([ inc_msg/1 + , inc_recv/1 , inc_sent/1 ]). @@ -313,6 +315,15 @@ update_counter(Name, Value) -> %% Inc received/sent metrics %%-------------------------------------------------------------------- +-spec(inc_msg(emqx_types:massage()) -> ok). +inc_msg(Msg) -> + case Msg#message.qos of + 0 -> inc('messages.qos0.received'); + 1 -> inc('messages.qos1.received'); + 2 -> inc('messages.qos2.received') + end, + inc('messages.received'). + %% @doc Inc packets received. -spec(inc_recv(emqx_types:packet()) -> ok). inc_recv(Packet) -> From 26c03aaa43c0e1e788ba8bb3eca1bb01eb665688 Mon Sep 17 00:00:00 2001 From: turtled Date: Wed, 11 Nov 2020 09:37:39 +0800 Subject: [PATCH 09/26] feat(connection): update alarm info --- src/emqx_connection.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/emqx_connection.erl b/src/emqx_connection.erl index f8bbd29fe..d52e79358 100644 --- a/src/emqx_connection.erl +++ b/src/emqx_connection.erl @@ -104,7 +104,8 @@ -define(ENABLED(X), (X =/= undefined)). -define(ALARM_TCP_CONGEST(Channel), - list_to_binary(io_lib:format("mqtt_conn/congested/~s", [emqx_channel:info(clientid, Channel)]))). + list_to_binary(io_lib:format("mqtt_conn/congested/~s/~s", [emqx_channel:info(clientid, Channel), maps:get(username, emqx_channel:info(clientinfo, Channel), <<"undefined">>)]))). + -dialyzer({no_match, [info/2]}). -dialyzer({nowarn_function, [ init/4 From 8cc69e639f0891278d0fd0707040d0c174cff73a Mon Sep 17 00:00:00 2001 From: terry-xiaoyu <506895667@qq.com> Date: Wed, 11 Nov 2020 14:35:21 +0800 Subject: [PATCH 10/26] fix(congestion): change the conn congestion alarm msg body --- src/emqx.appup.src | 6 ++++-- src/emqx_channel.erl | 14 ++++++++++++++ src/emqx_connection.erl | 25 ++++++++++++++++++++++--- 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/src/emqx.appup.src b/src/emqx.appup.src index 23a383d77..39c79d773 100644 --- a/src/emqx.appup.src +++ b/src/emqx.appup.src @@ -2,14 +2,16 @@ [ {"4.2.0", [ {load_module, emqx_alarm, brutal_purge, soft_purge, []}, - {load_module, emqx_connection, brutal_purge, soft_purge, []}, + {load_module, emqx_connection, brutal_purge, soft_purge, [emqx_channel]}, + {load_module, emqx_channel, brutal_purge, soft_purge, []}, {load_module, emqx_metrics, brutal_purge, soft_purge, []} ]} ], [ {"4.2.0", [ {load_module, emqx_alarm, brutal_purge, soft_purge, []}, - {load_module, emqx_connection, brutal_purge, soft_purge, []}, + {load_module, emqx_connection, brutal_purge, soft_purge, [emqx_channel]}, + {load_module, emqx_channel, brutal_purge, soft_purge, []}, {load_module, emqx_metrics, brutal_purge, soft_purge, []} ]} ] diff --git a/src/emqx_channel.erl b/src/emqx_channel.erl index 71b89f153..d9356ccd7 100644 --- a/src/emqx_channel.erl +++ b/src/emqx_channel.erl @@ -131,6 +131,20 @@ info(zone, #channel{clientinfo = #{zone := Zone}}) -> Zone; info(clientid, #channel{clientinfo = #{clientid := ClientId}}) -> ClientId; +info(username, #channel{clientinfo = #{username := Username}}) -> + Username; +info(socktype, #channel{conninfo = #{socktype := SockType}}) -> + SockType; +info(peername, #channel{conninfo = #{peername := Peername}}) -> + Peername; +info(sockname, #channel{conninfo = #{sockname := Sockname}}) -> + Sockname; +info(proto_name, #channel{conninfo = #{proto_name := ProtoName}}) -> + ProtoName; +info(proto_ver, #channel{conninfo = #{proto_ver := ProtoVer}}) -> + ProtoVer; +info(connected_at, #channel{conninfo = #{connected_at := ConnectedAt}}) -> + ConnectedAt; info(clientinfo, #channel{clientinfo = ClientInfo}) -> ClientInfo; info(session, #channel{session = Session}) -> diff --git a/src/emqx_connection.erl b/src/emqx_connection.erl index d52e79358..3078ebd91 100644 --- a/src/emqx_connection.erl +++ b/src/emqx_connection.erl @@ -107,6 +107,13 @@ list_to_binary(io_lib:format("mqtt_conn/congested/~s/~s", [emqx_channel:info(clientid, Channel), maps:get(username, emqx_channel:info(clientinfo, Channel), <<"undefined">>)]))). +-define(ALARM_CONN_INFO_KEYS, [ + socktype, sockname, peername, + clientid, username, proto_name, proto_ver, connected_at +]). +-define(ALARM_SOCK_STATS_KEYS, [send_pend, recv_cnt, recv_oct, send_cnt, send_oct]). +-define(ALARM_SOCK_OPTS_KEYS, [high_watermark, high_msgq_watermark, sndbuf, recbuf, buffer]). + -dialyzer({no_match, [info/2]}). -dialyzer({nowarn_function, [ init/4 , init_state/3 @@ -616,10 +623,9 @@ maybe_warn_congestion(Socket, Transport, Channel) -> IsCongestAlarmSet = is_congestion_alarm_set(), case is_congested(Socket, Transport) of true when not IsCongestAlarmSet -> - {ok, Stat} = Transport:getstat(Socket, [recv_cnt, recv_oct, send_cnt, send_oct]), - {ok, Opts} = Transport:getopts(Socket, [high_watermark,high_msgq_watermark, sndbuf, recbuf, buffer]), ok = set_congestion_alarm(), - emqx_alarm:activate(?ALARM_TCP_CONGEST(Channel), maps:from_list(Stat++Opts)); + emqx_alarm:activate(?ALARM_TCP_CONGEST(Channel), + tcp_congestion_alarm_details(Socket, Transport, Channel)); false when IsCongestAlarmSet -> ok = clear_congestion_alarm(), emqx_alarm:deactivate(?ALARM_TCP_CONGEST(Channel)); @@ -642,6 +648,19 @@ set_congestion_alarm() -> clear_congestion_alarm() -> erlang:put(conn_congested, false), ok. +tcp_congestion_alarm_details(Socket, Transport, Channel) -> + {ok, Stat} = Transport:getstat(Socket, ?ALARM_SOCK_STATS_KEYS), + {ok, Opts} = Transport:getopts(Socket, ?ALARM_SOCK_OPTS_KEYS), + SockInfo = maps:from_list(Stat ++ Opts), + ConnInfo = maps:from_list([conn_info(Key, Channel) || Key <- ?ALARM_CONN_INFO_KEYS]), + maps:merge(ConnInfo, SockInfo). + +conn_info(Key, Channel) when Key =:= sockname; Key =:= peername -> + {IPStr, Port} = emqx_channel:info(Key, Channel), + {Key, iolist_to_binary([inet:ntoa(IPStr),":",integer_to_list(Port)])}; +conn_info(Key, Channel) -> + {Key, emqx_channel:info(Key, Channel)}. + %%-------------------------------------------------------------------- %% Handle Info From 77d777fd116d5878b3f9a8cd11a1962d1b3d14e3 Mon Sep 17 00:00:00 2001 From: turtled Date: Fri, 13 Nov 2020 10:40:14 +0800 Subject: [PATCH 11/26] chore(acl): rollback emqx_access_rule module --- src/emqx_access_rule.erl | 153 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 src/emqx_access_rule.erl diff --git a/src/emqx_access_rule.erl b/src/emqx_access_rule.erl new file mode 100644 index 000000000..f0c6bcea9 --- /dev/null +++ b/src/emqx_access_rule.erl @@ -0,0 +1,153 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_access_rule). + +%% APIs +-export([ match/3 + , compile/1 + ]). + +-export_type([rule/0]). + +-type(acl_result() :: allow | deny). + +-type(who() :: all | binary() | + {client, binary()} | + {user, binary()} | + {ipaddr, esockd_cidr:cidr_string()}). + +-type(access() :: subscribe | publish | pubsub). + +-type(rule() :: {acl_result(), all} | + {acl_result(), who(), access(), list(emqx_topic:topic())}). + +-define(ALLOW_DENY(A), ((A =:= allow) orelse (A =:= deny))). +-define(PUBSUB(A), ((A =:= subscribe) orelse (A =:= publish) orelse (A =:= pubsub))). + +%% @doc Compile Access Rule. +compile({A, all}) when ?ALLOW_DENY(A) -> + {A, all}; + +compile({A, Who, Access, Topic}) when ?ALLOW_DENY(A), ?PUBSUB(Access), is_binary(Topic) -> + {A, compile(who, Who), Access, [compile(topic, Topic)]}; + +compile({A, Who, Access, TopicFilters}) when ?ALLOW_DENY(A), ?PUBSUB(Access) -> + {A, compile(who, Who), Access, [compile(topic, Topic) || Topic <- TopicFilters]}. + +compile(who, all) -> + all; +compile(who, {ipaddr, CIDR}) -> + {ipaddr, esockd_cidr:parse(CIDR, true)}; +compile(who, {client, all}) -> + {client, all}; +compile(who, {client, ClientId}) -> + {client, bin(ClientId)}; +compile(who, {user, all}) -> + {user, all}; +compile(who, {user, Username}) -> + {user, bin(Username)}; +compile(who, {'and', Conds}) when is_list(Conds) -> + {'and', [compile(who, Cond) || Cond <- Conds]}; +compile(who, {'or', Conds}) when is_list(Conds) -> + {'or', [compile(who, Cond) || Cond <- Conds]}; + +compile(topic, {eq, Topic}) -> + {eq, emqx_topic:words(bin(Topic))}; +compile(topic, Topic) -> + Words = emqx_topic:words(bin(Topic)), + case 'pattern?'(Words) of + true -> {pattern, Words}; + false -> Words + end. + +'pattern?'(Words) -> + lists:member(<<"%u">>, Words) + orelse lists:member(<<"%c">>, Words). + +bin(L) when is_list(L) -> + list_to_binary(L); +bin(B) when is_binary(B) -> + B. + +%% @doc Match access rule +-spec(match(emqx_types:clientinfo(), emqx_types:topic(), rule()) + -> {matched, allow} | {matched, deny} | nomatch). +match(_ClientInfo, _Topic, {AllowDeny, all}) when ?ALLOW_DENY(AllowDeny) -> + {matched, AllowDeny}; +match(ClientInfo, Topic, {AllowDeny, Who, _PubSub, TopicFilters}) + when ?ALLOW_DENY(AllowDeny) -> + case match_who(ClientInfo, Who) + andalso match_topics(ClientInfo, Topic, TopicFilters) of + true -> {matched, AllowDeny}; + false -> nomatch + end. + +match_who(_ClientInfo, all) -> + true; +match_who(_ClientInfo, {user, all}) -> + true; +match_who(_ClientInfo, {client, all}) -> + true; +match_who(#{clientid := ClientId}, {client, ClientId}) -> + true; +match_who(#{username := Username}, {user, Username}) -> + true; +match_who(#{peerhost := undefined}, {ipaddr, _Tup}) -> + false; +match_who(#{peerhost := IP}, {ipaddr, CIDR}) -> + esockd_cidr:match(IP, CIDR); +match_who(ClientInfo, {'and', Conds}) when is_list(Conds) -> + lists:foldl(fun(Who, Allow) -> + match_who(ClientInfo, Who) andalso Allow + end, true, Conds); +match_who(ClientInfo, {'or', Conds}) when is_list(Conds) -> + lists:foldl(fun(Who, Allow) -> + match_who(ClientInfo, Who) orelse Allow + end, false, Conds); +match_who(_ClientInfo, _Who) -> + false. + +match_topics(_ClientInfo, _Topic, []) -> + false; +match_topics(ClientInfo, Topic, [{pattern, PatternFilter}|Filters]) -> + TopicFilter = feed_var(ClientInfo, PatternFilter), + match_topic(emqx_topic:words(Topic), TopicFilter) + orelse match_topics(ClientInfo, Topic, Filters); +match_topics(ClientInfo, Topic, [TopicFilter|Filters]) -> + match_topic(emqx_topic:words(Topic), TopicFilter) + orelse match_topics(ClientInfo, Topic, Filters). + +match_topic(Topic, {eq, TopicFilter}) -> + Topic == TopicFilter; +match_topic(Topic, TopicFilter) -> + emqx_topic:match(Topic, TopicFilter). + +feed_var(ClientInfo, Pattern) -> + feed_var(ClientInfo, Pattern, []). +feed_var(_ClientInfo, [], Acc) -> + lists:reverse(Acc); +feed_var(ClientInfo = #{clientid := undefined}, [<<"%c">>|Words], Acc) -> + feed_var(ClientInfo, Words, [<<"%c">>|Acc]); +feed_var(ClientInfo = #{clientid := ClientId}, [<<"%c">>|Words], Acc) -> + feed_var(ClientInfo, Words, [ClientId |Acc]); +feed_var(ClientInfo = #{username := undefined}, [<<"%u">>|Words], Acc) -> + feed_var(ClientInfo, Words, [<<"%u">>|Acc]); +feed_var(ClientInfo = #{username := Username}, [<<"%u">>|Words], Acc) -> + feed_var(ClientInfo, Words, [Username|Acc]); +feed_var(ClientInfo, [W|Words], Acc) -> + feed_var(ClientInfo, Words, [W|Acc]). + From 3f41adc3bd98348738bd253cbb4622effb6985e3 Mon Sep 17 00:00:00 2001 From: zhanghongtong Date: Tue, 24 Nov 2020 01:47:45 +0000 Subject: [PATCH 12/26] chore(appup): update version --- src/emqx.appup.src | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/emqx.appup.src b/src/emqx.appup.src index 39c79d773..0900bb449 100644 --- a/src/emqx.appup.src +++ b/src/emqx.appup.src @@ -1,5 +1,11 @@ -{"4.2.1", +{"4.2.2", [ + {"4.2.1", [ + {load_module, emqx_alarm, brutal_purge, soft_purge, []}, + {load_module, emqx_connection, brutal_purge, soft_purge, [emqx_channel]}, + {load_module, emqx_channel, brutal_purge, soft_purge, []}, + {load_module, emqx_metrics, brutal_purge, soft_purge, []} + ]}, {"4.2.0", [ {load_module, emqx_alarm, brutal_purge, soft_purge, []}, {load_module, emqx_connection, brutal_purge, soft_purge, [emqx_channel]}, @@ -8,6 +14,12 @@ ]} ], [ + {"4.2.1", [ + {load_module, emqx_alarm, brutal_purge, soft_purge, []}, + {load_module, emqx_connection, brutal_purge, soft_purge, [emqx_channel]}, + {load_module, emqx_channel, brutal_purge, soft_purge, []}, + {load_module, emqx_metrics, brutal_purge, soft_purge, []} + ]}, {"4.2.0", [ {load_module, emqx_alarm, brutal_purge, soft_purge, []}, {load_module, emqx_connection, brutal_purge, soft_purge, [emqx_channel]}, From 3ca39cb9cb19214afc09d2b9350eb7aa38ba4e7e Mon Sep 17 00:00:00 2001 From: turtleDeng Date: Wed, 25 Nov 2020 10:27:56 +0800 Subject: [PATCH 13/26] chore(appup): update version --- src/emqx.appup.src | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/emqx.appup.src b/src/emqx.appup.src index 0900bb449..9a88fb938 100644 --- a/src/emqx.appup.src +++ b/src/emqx.appup.src @@ -1,11 +1,6 @@ {"4.2.2", [ - {"4.2.1", [ - {load_module, emqx_alarm, brutal_purge, soft_purge, []}, - {load_module, emqx_connection, brutal_purge, soft_purge, [emqx_channel]}, - {load_module, emqx_channel, brutal_purge, soft_purge, []}, - {load_module, emqx_metrics, brutal_purge, soft_purge, []} - ]}, + {"4.2.1", []}, {"4.2.0", [ {load_module, emqx_alarm, brutal_purge, soft_purge, []}, {load_module, emqx_connection, brutal_purge, soft_purge, [emqx_channel]}, @@ -14,12 +9,7 @@ ]} ], [ - {"4.2.1", [ - {load_module, emqx_alarm, brutal_purge, soft_purge, []}, - {load_module, emqx_connection, brutal_purge, soft_purge, [emqx_channel]}, - {load_module, emqx_channel, brutal_purge, soft_purge, []}, - {load_module, emqx_metrics, brutal_purge, soft_purge, []} - ]}, + {"4.2.1", []}, {"4.2.0", [ {load_module, emqx_alarm, brutal_purge, soft_purge, []}, {load_module, emqx_connection, brutal_purge, soft_purge, [emqx_channel]}, From aa7124f3c9703d69f7bdd4ac976a067dad98935d Mon Sep 17 00:00:00 2001 From: Shawn <506895667@qq.com> Date: Wed, 25 Nov 2020 14:40:25 +0800 Subject: [PATCH 14/26] chore(appup): use binding var 'VSN' --- src/emqx.appup.src | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/emqx.appup.src b/src/emqx.appup.src index 9a88fb938..cf25e1196 100644 --- a/src/emqx.appup.src +++ b/src/emqx.appup.src @@ -1,4 +1,4 @@ -{"4.2.2", +{VSN, [ {"4.2.1", []}, {"4.2.0", [ From f5162562a45187986ad26dc293c4dbcbd8f95880 Mon Sep 17 00:00:00 2001 From: Shawn <506895667@qq.com> Date: Thu, 26 Nov 2020 10:42:35 +0800 Subject: [PATCH 15/26] chore(appup): update appup --- src/emqx.appup.src | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/emqx.appup.src b/src/emqx.appup.src index cf25e1196..9a9d221aa 100644 --- a/src/emqx.appup.src +++ b/src/emqx.appup.src @@ -1,12 +1,12 @@ {VSN, [ - {"4.2.1", []}, {"4.2.0", [ {load_module, emqx_alarm, brutal_purge, soft_purge, []}, {load_module, emqx_connection, brutal_purge, soft_purge, [emqx_channel]}, {load_module, emqx_channel, brutal_purge, soft_purge, []}, {load_module, emqx_metrics, brutal_purge, soft_purge, []} - ]} + ]}, + {<<".*">>, []} ], [ {"4.2.1", []}, @@ -15,6 +15,7 @@ {load_module, emqx_connection, brutal_purge, soft_purge, [emqx_channel]}, {load_module, emqx_channel, brutal_purge, soft_purge, []}, {load_module, emqx_metrics, brutal_purge, soft_purge, []} - ]} + ]}, + {<<".*">>, []} ] }. From a3cdae3b42d9b55201b3927f23d3d93c9505f401 Mon Sep 17 00:00:00 2001 From: zhanghongtong Date: Mon, 30 Nov 2020 17:28:03 +0800 Subject: [PATCH 16/26] feat(listener): add depth for ssl listener --- .gitignore | 1 + etc/listeners.conf | 7 ++++++- priv/emqx.schema | 6 ++++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index aaec950d4..2e19823c3 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,4 @@ erlang.mk *.coverdata etc/emqx.conf.rendered Mnesia.*/ +.stamp diff --git a/etc/listeners.conf b/etc/listeners.conf index c7d11967d..034321b2a 100644 --- a/etc/listeners.conf +++ b/etc/listeners.conf @@ -301,6 +301,11 @@ listener.ssl.external.access.1 = allow all ## Value: Duration listener.ssl.external.handshake_timeout = 15s +## Maximum number of non-self-issued intermediate certificates that can follow the peer certificate in a valid certification path. +## +## Value: Number +## listener.ssl.external.depth = 10 + ## Path to the file containing the user's private PEM-encoded key. ## ## See: http://erlang.org/doc/man/ssl.html @@ -932,4 +937,4 @@ listener.wss.external.send_timeout_close = on ## Whether a WebSocket message is allowed to contain multiple MQTT packets ## ## Value: single | multiple -listener.wss.external.mqtt_piggyback = multiple \ No newline at end of file +listener.wss.external.mqtt_piggyback = multiple diff --git a/priv/emqx.schema b/priv/emqx.schema index 21047aaaa..830a0e934 100644 --- a/priv/emqx.schema +++ b/priv/emqx.schema @@ -1372,6 +1372,11 @@ end}. {datatype, {duration, ms}} ]}. +{mapping, "listener.ssl.$name.depth", "emqx.listeners", [ + {default, 10}, + {datatype, integer} +]}. + {mapping, "listener.ssl.$name.dhfile", "emqx.listeners", [ {datatype, string} ]}. @@ -1890,6 +1895,7 @@ end}. {ciphers, Ciphers}, {user_lookup_fun, UserLookupFun}, {handshake_timeout, cuttlefish:conf_get(Prefix ++ ".handshake_timeout", Conf, undefined)}, + {depth, cuttlefish:conf_get(Prefix ++ ".depth", Conf, undefined)}, {dhfile, cuttlefish:conf_get(Prefix ++ ".dhfile", Conf, undefined)}, {keyfile, cuttlefish:conf_get(Prefix ++ ".keyfile", Conf, undefined)}, {certfile, cuttlefish:conf_get(Prefix ++ ".certfile", Conf, undefined)}, From 40d272815a73833032f24586f333133f223296ed Mon Sep 17 00:00:00 2001 From: JianBo He Date: Wed, 2 Dec 2020 14:07:55 +0800 Subject: [PATCH 17/26] refactor(limiter): not saving anonymous function --- src/emqx_limiter.erl | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/emqx_limiter.erl b/src/emqx_limiter.erl index e3ff7512f..26172f761 100644 --- a/src/emqx_limiter.erl +++ b/src/emqx_limiter.erl @@ -35,7 +35,7 @@ -type(checker() :: #{ name := name() , capacity := non_neg_integer() , interval := non_neg_integer() - , consumer := function() | esockd_rate_limit:bucket() + , consumer := esockd_rate_limit:bucket() | emqx_zone:zone() }). -type(name() :: conn_bytes_in @@ -84,7 +84,7 @@ do_init_checker(Zone, {Name, {Capacity, Interval}}) -> _ -> esockd_limiter:create({Zone, Name}, Capacity, Interval) end, - Ck#{consumer => fun(I) -> esockd_limiter:consume({Zone, Name}, I) end}; + Ck#{consumer => Zone}; _ -> Ck#{consumer => esockd_rate_limit:new(Capacity / Interval, Capacity)} end. @@ -126,7 +126,13 @@ consume(Pubs, Bytes, #{name := Name, consumer := Cons}) -> _ -> case is_overall_limiter(Name) of true -> - {_, Intv} = Cons(Tokens), + {_, Intv} = case erlang:is_function(Cons) of + true -> %% Compatible with hot-upgrade from e4.2.0, e4.2.1. + %% It should be removed after 4.3.0 + {env, [Zone|_]} = erlang:fun_info(Cons, env), + esockd_limiter:consume({Zone, Name}, Tokens); + _ -> esockd_limiter:consume({Cons, Name}, Tokens) + end, {Intv, Cons}; _ -> esockd_rate_limit:check(Tokens, Cons) From 80c503fd64f73ac8b2650c9afa5bdee72c3121f4 Mon Sep 17 00:00:00 2001 From: JianBo He Date: Wed, 2 Dec 2020 17:00:23 +0800 Subject: [PATCH 18/26] refactor(conn): not saving anonymous func --- src/emqx.appup.src | 37 +++++++++-- src/emqx_connection.erl | 10 +-- src/emqx_frame.erl | 124 +++++++++++++++++++++++++++++++++++++ src/emqx_limiter.erl | 2 + src/emqx_ws_connection.erl | 10 +-- test/emqx_frame_SUITE.erl | 15 ++++- 6 files changed, 182 insertions(+), 16 deletions(-) diff --git a/src/emqx.appup.src b/src/emqx.appup.src index 9a9d221aa..8070ffc13 100644 --- a/src/emqx.appup.src +++ b/src/emqx.appup.src @@ -1,20 +1,47 @@ +%% -*-: erlang -*- + {VSN, [ {"4.2.0", [ {load_module, emqx_alarm, brutal_purge, soft_purge, []}, - {load_module, emqx_connection, brutal_purge, soft_purge, [emqx_channel]}, {load_module, emqx_channel, brutal_purge, soft_purge, []}, - {load_module, emqx_metrics, brutal_purge, soft_purge, []} + {load_module, emqx_metrics, brutal_purge, soft_purge, []}, + {load_module, emqx_limiter, brutal_purge, soft_purge, []}, + {suspend, [emqx_connection, emqx_ws_connection]}, + {load_module, emqx_frame, brutal_purge, soft_purge, []}, + {update, emqx_connection, {advanced, []}}, + {update, emqx_ws_connection, {advanced, []}}, + {resume, [emqx_connection, emqx_ws_connection]} + ]}, + {"4.2.1", [ + {load_module, emqx_limiter, brutal_purge, soft_purge, []}, + {suspend, [emqx_connection, emqx_ws_connection]}, + {load_module, emqx_frame, brutal_purge, soft_purge, []}, + {update, emqx_connection, {advanced, []}}, + {update, emqx_ws_connection, {advanced, []}}, + {resume, [emqx_connection, emqx_ws_connection]} ]}, {<<".*">>, []} ], [ - {"4.2.1", []}, {"4.2.0", [ {load_module, emqx_alarm, brutal_purge, soft_purge, []}, - {load_module, emqx_connection, brutal_purge, soft_purge, [emqx_channel]}, {load_module, emqx_channel, brutal_purge, soft_purge, []}, - {load_module, emqx_metrics, brutal_purge, soft_purge, []} + {load_module, emqx_metrics, brutal_purge, soft_purge, []}, + {load_module, emqx_limiter, brutal_purge, soft_purge, []}, + {suspend, [emqx_connection, emqx_ws_connection]}, + {load_module, emqx_frame, brutal_purge, soft_purge, []}, + {update, emqx_connection, {advanced, []}}, + {update, emqx_ws_connection, {advanced, []}}, + {resume, [emqx_connection, emqx_ws_connection]} + ]}, + {"4.2.1", [ + {load_module, emqx_limiter, brutal_purge, soft_purge, []}, + {suspend, [emqx_connection, emqx_ws_connection]}, + {load_module, emqx_frame, brutal_purge, soft_purge, []}, + {update, emqx_connection, {advanced, []}}, + {update, emqx_ws_connection, {advanced, []}}, + {resume, [emqx_connection, emqx_ws_connection]} ]}, {<<".*">>, []} ] diff --git a/src/emqx_connection.erl b/src/emqx_connection.erl index 3078ebd91..6f66386bc 100644 --- a/src/emqx_connection.erl +++ b/src/emqx_connection.erl @@ -80,8 +80,8 @@ limit_timer :: maybe(reference()), %% Parse State parse_state :: emqx_frame:parse_state(), - %% Serialize function - serialize :: emqx_frame:serialize_fun(), + %% Serialize options + serialize :: emqx_frame:serialize_opts(), %% Channel State channel :: emqx_channel:channel(), %% GC State @@ -214,7 +214,7 @@ init_state(Transport, Socket, Options) -> Limiter = emqx_limiter:init(Zone, PubLimit, BytesIn, RateLimit), FrameOpts = emqx_zone:mqtt_frame_options(Zone), ParseState = emqx_frame:initial_parse_state(FrameOpts), - Serialize = emqx_frame:serialize_fun(), + Serialize = emqx_frame:serialize_opts(), Channel = emqx_channel:init(ConnInfo, Options), GcState = emqx_zone:init_gc_state(Zone), StatsTimer = emqx_zone:stats_timer(Zone), @@ -348,7 +348,7 @@ handle_msg({Inet, _Sock, Data}, State) when Inet == tcp; Inet == ssl -> handle_msg({incoming, Packet = ?CONNECT_PACKET(ConnPkt)}, State = #state{idle_timer = IdleTimer}) -> ok = emqx_misc:cancel_timer(IdleTimer), - Serialize = emqx_frame:serialize_fun(ConnPkt), + Serialize = emqx_frame:serialize_opts(ConnPkt), NState = State#state{serialize = Serialize, idle_timer = undefined }, @@ -590,7 +590,7 @@ handle_outgoing(Packet, State) -> serialize_and_inc_stats_fun(#state{serialize = Serialize}) -> fun(Packet) -> - case Serialize(Packet) of + case emqx_frame:serialize_pkt(Packet, Serialize) of <<>> -> ?LOG(warning, "~s is discarded due to the frame is too large!", [emqx_packet:format(Packet)]), ok = emqx_metrics:inc('delivery.dropped.too_large'), diff --git a/src/emqx_frame.erl b/src/emqx_frame.erl index 1c27548f7..cd41a935c 100644 --- a/src/emqx_frame.erl +++ b/src/emqx_frame.erl @@ -31,26 +31,53 @@ , serialize/2 ]). +%% The new version APIs to avoid saving +%% anonymous func +-export([ parse2/1 + , parse2/2 + , serialize_opts/0 + , serialize_opts/1 + , serialize_pkt/2 + ]). + -export_type([ options/0 , parse_state/0 , parse_result/0 , serialize_fun/0 ]). +-export_type([ parse_state2/0 + , parse_result2/0 + , serialize_opts/0 + ]). + -type(options() :: #{strict_mode => boolean(), max_size => 1..?MAX_PACKET_SIZE, version => emqx_types:version() }). -type(parse_state() :: {none, options()} | cont_fun()). +-type(parse_state2() :: {none, options()} | {cont_state(), options()}). -type(parse_result() :: {more, cont_fun()} | {ok, emqx_types:packet(), binary(), parse_state()}). +-type(parse_result2() :: {more, parse_state()} + | {ok, emqx_types:packet(), binary(), parse_state()}). + -type(cont_fun() :: fun((binary()) -> parse_result())). +-type(cont_state() :: {Stage :: len | body, + State :: #{hdr := #mqtt_packet_header{}, + len := {pos_integer(), non_neg_integer()} | non_neg_integer(), + rest => binary() + } + }). + -type(serialize_fun() :: fun((emqx_types:packet()) -> iodata())). +-type(serialize_opts() :: options()). + -define(none(Options), {none, Options}). -define(DEFAULT_OPTIONS, @@ -81,6 +108,89 @@ merge_opts(Options) -> %% Parse MQTT Frame %%-------------------------------------------------------------------- +-spec(parse2(binary()) -> parse_result2()). +parse2(Bin) -> + parse2(Bin, initial_parse_state()). + +-spec(parse2(binary(), parse_state()) -> parse_result2()). +parse2(<<>>, {none, Options}) -> + {more, {none, Options}}; +parse2(<>, + {none, Options = #{strict_mode := StrictMode}}) -> + %% Validate header if strict mode. + StrictMode andalso validate_header(Type, Dup, QoS, Retain), + Header = #mqtt_packet_header{type = Type, + dup = bool(Dup), + qos = QoS, + retain = bool(Retain) + }, + Header1 = case fixqos(Type, QoS) of + QoS -> Header; + FixedQoS -> Header#mqtt_packet_header{qos = FixedQoS} + end, + parse_remaining_len2(Rest, Header1, Options); + +parse2(Bin, {{len, #{hdr := Header, + len := {Multiplier, Length}} + }, Options}) when is_binary(Bin) -> + parse_remaining_len2(Bin, Header, Multiplier, Length, Options); +parse2(Bin, {{body, #{hdr := Header, + len := Length, + rest := Rest} + }, Options}) when is_binary(Bin) -> + parse_frame2(<>, Header, Length, Options). + +parse_remaining_len2(<<>>, Header, Options) -> + {more, {{len, #{hdr => Header, len => {1, 0}}}, Options}}; +parse_remaining_len2(Rest, Header, Options) -> + parse_remaining_len2(Rest, Header, 1, 0, Options). + +parse_remaining_len2(_Bin, _Header, _Multiplier, Length, #{max_size := MaxSize}) + when Length > MaxSize -> + error(frame_too_large); +parse_remaining_len2(<<>>, Header, Multiplier, Length, Options) -> + {more, {{len, #{hdr => Header, len => {Multiplier, Length}}}, Options}}; +%% Match DISCONNECT without payload +parse_remaining_len2(<<0:8, Rest/binary>>, Header = #mqtt_packet_header{type = ?DISCONNECT}, 1, 0, Options) -> + Packet = packet(Header, #mqtt_packet_disconnect{reason_code = ?RC_SUCCESS}), + {ok, Packet, Rest, ?none(Options)}; +%% Match PINGREQ. +parse_remaining_len2(<<0:8, Rest/binary>>, Header, 1, 0, Options) -> + parse_frame2(Rest, Header, 0, Options); +%% Match PUBACK, PUBREC, PUBREL, PUBCOMP, UNSUBACK... +parse_remaining_len2(<<0:1, 2:7, Rest/binary>>, Header, 1, 0, Options) -> + parse_frame2(Rest, Header, 2, Options); +parse_remaining_len2(<<1:1, Len:7, Rest/binary>>, Header, Multiplier, Value, Options) -> + parse_remaining_len2(Rest, Header, Multiplier * ?HIGHBIT, Value + Len * Multiplier, Options); +parse_remaining_len2(<<0:1, Len:7, Rest/binary>>, Header, Multiplier, Value, + Options = #{max_size := MaxSize}) -> + FrameLen = Value + Len * Multiplier, + if + FrameLen > MaxSize -> error(frame_too_large); + true -> parse_frame2(Rest, Header, FrameLen, Options) + end. + +parse_frame2(Bin, Header, 0, Options) -> + {ok, packet(Header), Bin, ?none(Options)}; + +parse_frame2(Bin, Header, Length, Options) -> + case Bin of + <> -> + case parse_packet(Header, FrameBin, Options) of + {Variable, Payload} -> + {ok, packet(Header, Variable, Payload), Rest, ?none(Options)}; + Variable = #mqtt_packet_connect{proto_ver = Ver} -> + {ok, packet(Header, Variable), Rest, ?none(Options#{version := Ver})}; + Variable -> + {ok, packet(Header, Variable), Rest, ?none(Options)} + end; + TooShortBin -> + {more, {{body, #{hdr => Header, len => Length, rest => TooShortBin}}, Options}} + end. + +%% Deprecated parse funcs +%% It should be removed after 4.2.x + -spec(parse(binary()) -> parse_result()). parse(Bin) -> parse(Bin, initial_parse_state()). @@ -443,6 +553,20 @@ serialize_fun(#{version := Ver, max_size := MaxSize}) -> end end. +serialize_opts() -> + ?DEFAULT_OPTIONS. + +serialize_opts(#mqtt_packet_connect{proto_ver = ProtoVer, properties = ConnProps}) -> + MaxSize = get_property('Maximum-Packet-Size', ConnProps, ?MAX_PACKET_SIZE), + #{version => ProtoVer, max_size => MaxSize}. + +serialize_pkt(Packet, #{version := Ver, max_size := MaxSize}) -> + IoData = serialize(Packet, Ver), + case is_too_large(IoData, MaxSize) of + true -> <<>>; + false -> IoData + end. + -spec(serialize(emqx_types:packet()) -> iodata()). serialize(Packet) -> serialize(Packet, ?MQTT_PROTO_V4). diff --git a/src/emqx_limiter.erl b/src/emqx_limiter.erl index 26172f761..fad897d92 100644 --- a/src/emqx_limiter.erl +++ b/src/emqx_limiter.erl @@ -53,6 +53,8 @@ -type(limiter() :: #limiter{}). +-dialyzer({nowarn_function, [consume/3]}). + %%-------------------------------------------------------------------- %% APIs %%-------------------------------------------------------------------- diff --git a/src/emqx_ws_connection.erl b/src/emqx_ws_connection.erl index 33b882768..ee0e24dbb 100644 --- a/src/emqx_ws_connection.erl +++ b/src/emqx_ws_connection.erl @@ -70,8 +70,8 @@ limit_timer :: maybe(reference()), %% Parse State parse_state :: emqx_frame:parse_state(), - %% Serialize Fun - serialize :: emqx_frame:serialize_fun(), + %% Serialize options + serialize :: emqx_frame:serialize_opts(), %% Channel channel :: emqx_channel:channel(), %% GC State @@ -231,7 +231,7 @@ websocket_init([Req, Opts]) -> MQTTPiggyback = proplists:get_value(mqtt_piggyback, Opts, multiple), FrameOpts = emqx_zone:mqtt_frame_options(Zone), ParseState = emqx_frame:initial_parse_state(FrameOpts), - Serialize = emqx_frame:serialize_fun(), + Serialize = emqx_frame:serialize_opts(), Channel = emqx_channel:init(ConnInfo, Opts), GcState = emqx_zone:init_gc_state(Zone), StatsTimer = emqx_zone:stats_timer(Zone), @@ -292,7 +292,7 @@ websocket_info({cast, Msg}, State) -> handle_info(Msg, State); websocket_info({incoming, Packet = ?CONNECT_PACKET(ConnPkt)}, State) -> - Serialize = emqx_frame:serialize_fun(ConnPkt), + Serialize = emqx_frame:serialize_opts(ConnPkt), NState = State#state{serialize = Serialize}, handle_incoming(Packet, cancel_idle_timer(NState)); @@ -544,7 +544,7 @@ handle_outgoing(Packets, State = #state{active_n = ActiveN, mqtt_piggyback = MQT serialize_and_inc_stats_fun(#state{serialize = Serialize}) -> fun(Packet) -> - case Serialize(Packet) of + case emqx_frame:serialize_pkt(Packet, Serialize) of <<>> -> ?LOG(warning, "~s is discarded due to the frame is too large.", [emqx_packet:format(Packet)]), ok = emqx_metrics:inc('delivery.dropped.too_large'), diff --git a/test/emqx_frame_SUITE.erl b/test/emqx_frame_SUITE.erl index 5d4e146db..7ef60bc69 100644 --- a/test/emqx_frame_SUITE.erl +++ b/test/emqx_frame_SUITE.erl @@ -26,6 +26,7 @@ all() -> [{group, parse}, + {group, parse2}, {group, connect}, {group, connack}, {group, publish}, @@ -44,6 +45,8 @@ groups() -> [t_parse_cont, t_parse_frame_too_large ]}, + {parse2, [parallel], + [t_parse_cont2]}, {connect, [parallel], [t_serialize_parse_v3_connect, t_serialize_parse_v4_connect, @@ -129,6 +132,16 @@ t_parse_frame_too_large(_) -> ?catch_error(frame_too_large, parse_serialize(Packet, #{max_size => 512})), ?assertEqual(Packet, parse_serialize(Packet, #{max_size => 2048, version => ?MQTT_PROTO_V4})). +t_parse_cont2(_) -> + Packet = ?CONNECT_PACKET(#mqtt_packet_connect{}), + ParseState = emqx_frame:initial_parse_state(), + <> = serialize_to_binary(Packet), + {more, ContParse} = emqx_frame:parse2(<<>>, ParseState), + {more, ContParse1} = emqx_frame:parse2(HdrBin, ContParse), + {more, ContParse2} = emqx_frame:parse2(LenBin, ContParse1), + {more, ContParse3} = emqx_frame:parse2(<<>>, ContParse2), + {ok, Packet, <<>>, _} = emqx_frame:parse2(RestBin, ContParse3). + t_serialize_parse_v3_connect(_) -> Bin = <<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, @@ -509,7 +522,7 @@ parse_serialize(Packet, Opts) when is_map(Opts) -> Ver = maps:get(version, Opts, ?MQTT_PROTO_V4), Bin = iolist_to_binary(emqx_frame:serialize(Packet, Ver)), ParseState = emqx_frame:initial_parse_state(Opts), - {ok, NPacket, <<>>, _} = emqx_frame:parse(Bin, ParseState), + {ok, NPacket, <<>>, _} = emqx_frame:parse2(Bin, ParseState), NPacket. serialize_to_binary(Packet) -> From ca0f6b69a447a245345bf9d1854dbafe8781546d Mon Sep 17 00:00:00 2001 From: JianBo He Date: Wed, 2 Dec 2020 17:52:36 +0800 Subject: [PATCH 19/26] chore: supply the code_change logic --- rebar.config | 2 +- src/emqx_connection.erl | 56 ++++++++++++++++ src/emqx_frame.erl | 133 ++++++-------------------------------- src/emqx_limiter.erl | 8 +-- test/emqx_frame_SUITE.erl | 15 +---- test/emqx_vm_SUITE.erl | 2 +- 6 files changed, 78 insertions(+), 138 deletions(-) diff --git a/rebar.config b/rebar.config index df71f9762..fc3a95ce2 100644 --- a/rebar.config +++ b/rebar.config @@ -40,7 +40,7 @@ [{plugins, [{coveralls, {git, "https://github.com/emqx/coveralls-erl", {branch, "github"}}}]}, {deps, [{bbmustache, "1.7.0"}, - {emqtt, {git, "https://github.com/emqx/emqtt", {tag, "1.2.0"}}}, + {emqtt, {git, "https://github.com/emqx/emqtt", {tag, "1.2.3"}}}, {emqx_ct_helpers, {git, "https://github.com/emqx/emqx-ct-helpers", {tag, "1.3.0"}}} ]}, {erl_opts, [debug_info]} diff --git a/src/emqx_connection.erl b/src/emqx_connection.erl index 6f66386bc..7d055a14e 100644 --- a/src/emqx_connection.erl +++ b/src/emqx_connection.erl @@ -453,6 +453,62 @@ system_continue(Parent, _Debug, State) -> system_terminate(Reason, _Parent, _Debug, State) -> terminate(Reason, State). +system_code_change(State, _Mod, {down, Vsn}, _Extra) + when Vsn == "4.2.0"; + Vsn == "4.2.1" -> + Channel = State#state.channel, + NSerialize = emqx_frame:serialize_fun(State#state.serialize), + case {State#state.parse_state, element(10, Channel)} of + {{none, _}, undefined} -> + {ok, State#state{serialize = NSerialize}}; + {Ps, Quota} -> + %% BACKW: e4.2.0-e4.2.1 + %% We can't recover/reconstruct anonymous function state for + %% Parser or Quota consumer. So just close it. + ?LOG(error, "Unsupport downgrade connection ~0p, peername: ~0p." + " Due to it have an incomplete frame or unsafed quota counter," + " parser_state: ~0p, quota: ~0p." + " Force close it now!!!", [self(), State#state.peername, Ps, Quota]), + self() ! {close, unsupported_downgrade_connection_state}, + {ok, State#state{serialize = NSerialize}} + end; + +system_code_change(State, _Mod, Vsn, _Extra) + when Vsn == "4.2.0"; + Vsn == "4.2.1" -> + Channel = State#state.channel, + NChannel = + case element(10, Channel) of + undefined -> Channel; + Quoter -> + Zone = element(2, Quoter), + Cks = element(3, Quoter), + NCks = [case Name == overall_messages_routing of + true -> Ck#{consumer => Zone}; + _ -> Ck + end || Ck = #{name := Name} <- Cks], + setelement(10, Channel, setelement(3, Quoter, NCks)) + end, + + NParseState = + case State#state.parse_state of + Ps = {none, _} -> Ps; + Ps when is_function(Ps) -> + case erlang:fun_info(Ps, env) of + {_, [Hdr, Opts]} -> + {{len, #{hdr => Hdr, len => {1,0}}}, Opts}; + {_, [Bin, Hdr, Len, Opts]} when is_binary(Bin) -> + {{body, #{hdr => Hdr, len => Len, rest => Bin}}, Opts}; + {_, [Hdr, Multip, Len, Opts]} -> + {{len, #{hdr => Hdr, len => {Multip, Len}}}, Opts} + end + end, + + {_, [Ver, MaxSize]} = erlang:fun_info(State#state.serialize, env), + NSerialize = #{version => Ver, max_size => MaxSize}, + + {ok, State#state{channel = NChannel, parse_state = NParseState, serialize = NSerialize}}; + system_code_change(State, _Mod, _OldVsn, _Extra) -> {ok, State}. diff --git a/src/emqx_frame.erl b/src/emqx_frame.erl index cd41a935c..d09b8416e 100644 --- a/src/emqx_frame.erl +++ b/src/emqx_frame.erl @@ -27,27 +27,16 @@ , parse/2 , serialize_fun/0 , serialize_fun/1 - , serialize/1 - , serialize/2 - ]). - -%% The new version APIs to avoid saving -%% anonymous func --export([ parse2/1 - , parse2/2 , serialize_opts/0 , serialize_opts/1 , serialize_pkt/2 + , serialize/1 + , serialize/2 ]). -export_type([ options/0 , parse_state/0 , parse_result/0 - , serialize_fun/0 - ]). - --export_type([ parse_state2/0 - , parse_result2/0 , serialize_opts/0 ]). @@ -56,17 +45,11 @@ version => emqx_types:version() }). --type(parse_state() :: {none, options()} | cont_fun()). --type(parse_state2() :: {none, options()} | {cont_state(), options()}). +-type(parse_state() :: {none, options()} | {cont_state(), options()}). --type(parse_result() :: {more, cont_fun()} +-type(parse_result() :: {more, parse_state()} | {ok, emqx_types:packet(), binary(), parse_state()}). --type(parse_result2() :: {more, parse_state()} - | {ok, emqx_types:packet(), binary(), parse_state()}). - --type(cont_fun() :: fun((binary()) -> parse_result())). - -type(cont_state() :: {Stage :: len | body, State :: #{hdr := #mqtt_packet_header{}, len := {pos_integer(), non_neg_integer()} | non_neg_integer(), @@ -74,8 +57,6 @@ } }). --type(serialize_fun() :: fun((emqx_types:packet()) -> iodata())). - -type(serialize_opts() :: options()). -define(none(Options), {none, Options}). @@ -108,96 +89,13 @@ merge_opts(Options) -> %% Parse MQTT Frame %%-------------------------------------------------------------------- --spec(parse2(binary()) -> parse_result2()). -parse2(Bin) -> - parse2(Bin, initial_parse_state()). - --spec(parse2(binary(), parse_state()) -> parse_result2()). -parse2(<<>>, {none, Options}) -> - {more, {none, Options}}; -parse2(<>, - {none, Options = #{strict_mode := StrictMode}}) -> - %% Validate header if strict mode. - StrictMode andalso validate_header(Type, Dup, QoS, Retain), - Header = #mqtt_packet_header{type = Type, - dup = bool(Dup), - qos = QoS, - retain = bool(Retain) - }, - Header1 = case fixqos(Type, QoS) of - QoS -> Header; - FixedQoS -> Header#mqtt_packet_header{qos = FixedQoS} - end, - parse_remaining_len2(Rest, Header1, Options); - -parse2(Bin, {{len, #{hdr := Header, - len := {Multiplier, Length}} - }, Options}) when is_binary(Bin) -> - parse_remaining_len2(Bin, Header, Multiplier, Length, Options); -parse2(Bin, {{body, #{hdr := Header, - len := Length, - rest := Rest} - }, Options}) when is_binary(Bin) -> - parse_frame2(<>, Header, Length, Options). - -parse_remaining_len2(<<>>, Header, Options) -> - {more, {{len, #{hdr => Header, len => {1, 0}}}, Options}}; -parse_remaining_len2(Rest, Header, Options) -> - parse_remaining_len2(Rest, Header, 1, 0, Options). - -parse_remaining_len2(_Bin, _Header, _Multiplier, Length, #{max_size := MaxSize}) - when Length > MaxSize -> - error(frame_too_large); -parse_remaining_len2(<<>>, Header, Multiplier, Length, Options) -> - {more, {{len, #{hdr => Header, len => {Multiplier, Length}}}, Options}}; -%% Match DISCONNECT without payload -parse_remaining_len2(<<0:8, Rest/binary>>, Header = #mqtt_packet_header{type = ?DISCONNECT}, 1, 0, Options) -> - Packet = packet(Header, #mqtt_packet_disconnect{reason_code = ?RC_SUCCESS}), - {ok, Packet, Rest, ?none(Options)}; -%% Match PINGREQ. -parse_remaining_len2(<<0:8, Rest/binary>>, Header, 1, 0, Options) -> - parse_frame2(Rest, Header, 0, Options); -%% Match PUBACK, PUBREC, PUBREL, PUBCOMP, UNSUBACK... -parse_remaining_len2(<<0:1, 2:7, Rest/binary>>, Header, 1, 0, Options) -> - parse_frame2(Rest, Header, 2, Options); -parse_remaining_len2(<<1:1, Len:7, Rest/binary>>, Header, Multiplier, Value, Options) -> - parse_remaining_len2(Rest, Header, Multiplier * ?HIGHBIT, Value + Len * Multiplier, Options); -parse_remaining_len2(<<0:1, Len:7, Rest/binary>>, Header, Multiplier, Value, - Options = #{max_size := MaxSize}) -> - FrameLen = Value + Len * Multiplier, - if - FrameLen > MaxSize -> error(frame_too_large); - true -> parse_frame2(Rest, Header, FrameLen, Options) - end. - -parse_frame2(Bin, Header, 0, Options) -> - {ok, packet(Header), Bin, ?none(Options)}; - -parse_frame2(Bin, Header, Length, Options) -> - case Bin of - <> -> - case parse_packet(Header, FrameBin, Options) of - {Variable, Payload} -> - {ok, packet(Header, Variable, Payload), Rest, ?none(Options)}; - Variable = #mqtt_packet_connect{proto_ver = Ver} -> - {ok, packet(Header, Variable), Rest, ?none(Options#{version := Ver})}; - Variable -> - {ok, packet(Header, Variable), Rest, ?none(Options)} - end; - TooShortBin -> - {more, {{body, #{hdr => Header, len => Length, rest => TooShortBin}}, Options}} - end. - -%% Deprecated parse funcs -%% It should be removed after 4.2.x - -spec(parse(binary()) -> parse_result()). parse(Bin) -> parse(Bin, initial_parse_state()). -spec(parse(binary(), parse_state()) -> parse_result()). parse(<<>>, {none, Options}) -> - {more, fun(Bin) -> parse(Bin, {none, Options}) end}; + {more, {none, Options}}; parse(<>, {none, Options = #{strict_mode := StrictMode}}) -> %% Validate header if strict mode. @@ -212,11 +110,19 @@ parse(<>, FixedQoS -> Header#mqtt_packet_header{qos = FixedQoS} end, parse_remaining_len(Rest, Header1, Options); -parse(Bin, Cont) when is_binary(Bin), is_function(Cont) -> - Cont(Bin). + +parse(Bin, {{len, #{hdr := Header, + len := {Multiplier, Length}} + }, Options}) when is_binary(Bin) -> + parse_remaining_len(Bin, Header, Multiplier, Length, Options); +parse(Bin, {{body, #{hdr := Header, + len := Length, + rest := Rest} + }, Options}) when is_binary(Bin) -> + parse_frame(<>, Header, Length, Options). parse_remaining_len(<<>>, Header, Options) -> - {more, fun(Bin) -> parse_remaining_len(Bin, Header, Options) end}; + {more, {{len, #{hdr => Header, len => {1, 0}}}, Options}}; parse_remaining_len(Rest, Header, Options) -> parse_remaining_len(Rest, Header, 1, 0, Options). @@ -224,7 +130,7 @@ parse_remaining_len(_Bin, _Header, _Multiplier, Length, #{max_size := MaxSize}) when Length > MaxSize -> error(frame_too_large); parse_remaining_len(<<>>, Header, Multiplier, Length, Options) -> - {more, fun(Bin) -> parse_remaining_len(Bin, Header, Multiplier, Length, Options) end}; + {more, {{len, #{hdr => Header, len => {Multiplier, Length}}}, Options}}; %% Match DISCONNECT without payload parse_remaining_len(<<0:8, Rest/binary>>, Header = #mqtt_packet_header{type = ?DISCONNECT}, 1, 0, Options) -> Packet = packet(Header, #mqtt_packet_disconnect{reason_code = ?RC_SUCCESS}), @@ -260,9 +166,7 @@ parse_frame(Bin, Header, Length, Options) -> {ok, packet(Header, Variable), Rest, ?none(Options)} end; TooShortBin -> - {more, fun(BinMore) -> - parse_frame(<>, Header, Length, Options) - end} + {more, {{body, #{hdr => Header, len => Length, rest => TooShortBin}}, Options}} end. -compile({inline, [packet/1, packet/2, packet/3]}). @@ -870,4 +774,3 @@ fixqos(?PUBREL, 0) -> 1; fixqos(?SUBSCRIBE, 0) -> 1; fixqos(?UNSUBSCRIBE, 0) -> 1; fixqos(_Type, QoS) -> QoS. - diff --git a/src/emqx_limiter.erl b/src/emqx_limiter.erl index fad897d92..447e04fea 100644 --- a/src/emqx_limiter.erl +++ b/src/emqx_limiter.erl @@ -128,13 +128,7 @@ consume(Pubs, Bytes, #{name := Name, consumer := Cons}) -> _ -> case is_overall_limiter(Name) of true -> - {_, Intv} = case erlang:is_function(Cons) of - true -> %% Compatible with hot-upgrade from e4.2.0, e4.2.1. - %% It should be removed after 4.3.0 - {env, [Zone|_]} = erlang:fun_info(Cons, env), - esockd_limiter:consume({Zone, Name}, Tokens); - _ -> esockd_limiter:consume({Cons, Name}, Tokens) - end, + {_, Intv} = esockd_limiter:consume({Cons, Name}, Tokens), {Intv, Cons}; _ -> esockd_rate_limit:check(Tokens, Cons) diff --git a/test/emqx_frame_SUITE.erl b/test/emqx_frame_SUITE.erl index 7ef60bc69..5d4e146db 100644 --- a/test/emqx_frame_SUITE.erl +++ b/test/emqx_frame_SUITE.erl @@ -26,7 +26,6 @@ all() -> [{group, parse}, - {group, parse2}, {group, connect}, {group, connack}, {group, publish}, @@ -45,8 +44,6 @@ groups() -> [t_parse_cont, t_parse_frame_too_large ]}, - {parse2, [parallel], - [t_parse_cont2]}, {connect, [parallel], [t_serialize_parse_v3_connect, t_serialize_parse_v4_connect, @@ -132,16 +129,6 @@ t_parse_frame_too_large(_) -> ?catch_error(frame_too_large, parse_serialize(Packet, #{max_size => 512})), ?assertEqual(Packet, parse_serialize(Packet, #{max_size => 2048, version => ?MQTT_PROTO_V4})). -t_parse_cont2(_) -> - Packet = ?CONNECT_PACKET(#mqtt_packet_connect{}), - ParseState = emqx_frame:initial_parse_state(), - <> = serialize_to_binary(Packet), - {more, ContParse} = emqx_frame:parse2(<<>>, ParseState), - {more, ContParse1} = emqx_frame:parse2(HdrBin, ContParse), - {more, ContParse2} = emqx_frame:parse2(LenBin, ContParse1), - {more, ContParse3} = emqx_frame:parse2(<<>>, ContParse2), - {ok, Packet, <<>>, _} = emqx_frame:parse2(RestBin, ContParse3). - t_serialize_parse_v3_connect(_) -> Bin = <<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, @@ -522,7 +509,7 @@ parse_serialize(Packet, Opts) when is_map(Opts) -> Ver = maps:get(version, Opts, ?MQTT_PROTO_V4), Bin = iolist_to_binary(emqx_frame:serialize(Packet, Ver)), ParseState = emqx_frame:initial_parse_state(Opts), - {ok, NPacket, <<>>, _} = emqx_frame:parse2(Bin, ParseState), + {ok, NPacket, <<>>, _} = emqx_frame:parse(Bin, ParseState), NPacket. serialize_to_binary(Packet) -> diff --git a/test/emqx_vm_SUITE.erl b/test/emqx_vm_SUITE.erl index 19545f0ea..4bc231ad0 100644 --- a/test/emqx_vm_SUITE.erl +++ b/test/emqx_vm_SUITE.erl @@ -80,7 +80,7 @@ t_get_port_info(_Config) -> {ok, Sock} = gen_tcp:connect("localhost", 5678, [binary, {packet, 0}]), emqx_vm:get_port_info(), ok = gen_tcp:close(Sock), - [Port | _] = erlang:ports(). + [_Port | _] = erlang:ports(). t_transform_port(_Config) -> [Port | _] = erlang:ports(), From 7462ed92baea3935109591b581533de559f18cc9 Mon Sep 17 00:00:00 2001 From: JianBo He Date: Thu, 3 Dec 2020 17:13:06 +0800 Subject: [PATCH 20/26] chore: eliminate diaylzer warnings --- src/emqx_connection.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/emqx_connection.erl b/src/emqx_connection.erl index 7d055a14e..6e8ed9a46 100644 --- a/src/emqx_connection.erl +++ b/src/emqx_connection.erl @@ -119,6 +119,7 @@ , init_state/3 , run_loop/2 , system_terminate/4 + , system_code_change/4 ]}). -spec(start_link(esockd:transport(), esockd:socket(), proplists:proplist()) From f8369f5280890319be3b1749f0631c64c3f094a7 Mon Sep 17 00:00:00 2001 From: Turtle Date: Fri, 4 Dec 2020 21:39:27 +0800 Subject: [PATCH 21/26] test: comment the will message cases --- test/mqtt_protocol_v5_SUITE.erl | 110 +++++++++++++++++--------------- 1 file changed, 57 insertions(+), 53 deletions(-) diff --git a/test/mqtt_protocol_v5_SUITE.erl b/test/mqtt_protocol_v5_SUITE.erl index f0a0cc3dc..2291605e0 100644 --- a/test/mqtt_protocol_v5_SUITE.erl +++ b/test/mqtt_protocol_v5_SUITE.erl @@ -328,59 +328,63 @@ t_connect_session_expiry_interval(_) -> ok = emqtt:disconnect(Client3). %% [MQTT-3.1.3-9] -t_connect_will_delay_interval(_) -> - process_flag(trap_exit, true), - Topic = nth(1, ?TOPICS), - Payload = "will message", - - {ok, Client1} = emqtt:start_link([{proto_ver, v5}]), - {ok, _} = emqtt:connect(Client1), - {ok, _, [2]} = emqtt:subscribe(Client1, Topic, qos2), - - {ok, Client2} = emqtt:start_link([ - {clientid, <<"t_connect_will_delay_interval">>}, - {proto_ver, v5}, - {clean_start, true}, - {will_flag, true}, - {will_qos, 2}, - {will_topic, Topic}, - {will_payload, Payload}, - {will_props, #{'Will-Delay-Interval' => 3}}, - {properties, #{'Session-Expiry-Interval' => 7200}}, - {keepalive, 2} - ]), - {ok, _} = emqtt:connect(Client2), - - timer:sleep(5000), - ?assertEqual(0, length(receive_messages(1))), - timer:sleep(7000), - ?assertEqual(1, length(receive_messages(1))), - - {ok, Client3} = emqtt:start_link([ - {clientid, <<"t_connect_will_delay_interval">>}, - {proto_ver, v5}, - {clean_start, true}, - {will_flag, true}, - {will_qos, 2}, - {will_topic, Topic}, - {will_payload, Payload}, - {will_props, #{'Will-Delay-Interval' => 7200}}, - {properties, #{'Session-Expiry-Interval' => 3}}, - {keepalive, 2} - ]), - {ok, _} = emqtt:connect(Client3), - - timer:sleep(5000), - ?assertEqual(0, length(receive_messages(1))), - timer:sleep(7000), - ?assertEqual(1, length(receive_messages(1))), - - ok = emqtt:disconnect(Client1), - - receive {'EXIT', _, _} -> ok - after 100 -> ok - end, - process_flag(trap_exit, false). +%% !!!REFACTOR NEED: +%t_connect_will_delay_interval(_) -> +% process_flag(trap_exit, true), +% Topic = nth(1, ?TOPICS), +% Payload = "will message", +% +% {ok, Client1} = emqtt:start_link([{proto_ver, v5}]), +% {ok, _} = emqtt:connect(Client1), +% {ok, _, [2]} = emqtt:subscribe(Client1, Topic, qos2), +% +% {ok, Client2} = emqtt:start_link([ +% {clientid, <<"t_connect_will_delay_interval">>}, +% {proto_ver, v5}, +% {clean_start, true}, +% {will_flag, true}, +% {will_qos, 2}, +% {will_topic, Topic}, +% {will_payload, Payload}, +% {will_props, #{'Will-Delay-Interval' => 3}}, +% {properties, #{'Session-Expiry-Interval' => 7200}}, +% {keepalive, 2} +% ]), +% {ok, _} = emqtt:connect(Client2), +% timer:sleep(50), +% erlang:exit(Client2, kill), +% timer:sleep(2000), +% ?assertEqual(0, length(receive_messages(1))), +% timer:sleep(5000), +% ?assertEqual(1, length(receive_messages(1))), +% +% {ok, Client3} = emqtt:start_link([ +% {clientid, <<"t_connect_will_delay_interval">>}, +% {proto_ver, v5}, +% {clean_start, true}, +% {will_flag, true}, +% {will_qos, 2}, +% {will_topic, Topic}, +% {will_payload, Payload}, +% {will_props, #{'Will-Delay-Interval' => 7200}}, +% {properties, #{'Session-Expiry-Interval' => 3}}, +% {keepalive, 2} +% ]), +% {ok, _} = emqtt:connect(Client3), +% timer:sleep(50), +% erlang:exit(Client3, kill), +% +% timer:sleep(2000), +% ?assertEqual(0, length(receive_messages(1))), +% timer:sleep(5000), +% ?assertEqual(1, length(receive_messages(1))), +% +% ok = emqtt:disconnect(Client1), +% +% receive {'EXIT', _, _} -> ok +% after 100 -> ok +% end, +% process_flag(trap_exit, false). %% [MQTT-3.1.4-3] t_connect_duplicate_clientid(_) -> From 042b75adc77066e692ea1e1ff2b0572d65fe715a Mon Sep 17 00:00:00 2001 From: zhouzb Date: Fri, 4 Dec 2020 10:45:55 +0800 Subject: [PATCH 22/26] fix(ekka): update ekka version to fix oom --- rebar.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rebar.config b/rebar.config index fc3a95ce2..a7ae1409f 100644 --- a/rebar.config +++ b/rebar.config @@ -7,7 +7,7 @@ {jiffy, {git, "https://github.com/emqx/jiffy", {tag, "1.0.5"}}}, {cowboy, {git, "https://github.com/emqx/cowboy", {tag, "2.7.1"}}}, {esockd, {git, "https://github.com/emqx/esockd", {tag, "5.7.4"}}}, - {ekka, {git, "https://github.com/emqx/ekka", {tag, "0.7.4"}}}, + {ekka, {git, "https://github.com/emqx/ekka", {tag, "0.7.5"}}}, {gen_rpc, {git, "https://github.com/emqx/gen_rpc", {tag, "2.5.0"}}}, {cuttlefish, {git, "https://github.com/emqx/cuttlefish", {tag, "v3.0.0"}}} ]}. From 2e7ec25ae292aa63be79b6e8edb51ec1b7582a08 Mon Sep 17 00:00:00 2001 From: JianBo He Date: Sat, 5 Dec 2020 15:04:14 +0800 Subject: [PATCH 23/26] chore(appup): suspend esockd_acceptor --- src/emqx.appup.src | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/emqx.appup.src b/src/emqx.appup.src index 8070ffc13..ed4b0695f 100644 --- a/src/emqx.appup.src +++ b/src/emqx.appup.src @@ -7,19 +7,19 @@ {load_module, emqx_channel, brutal_purge, soft_purge, []}, {load_module, emqx_metrics, brutal_purge, soft_purge, []}, {load_module, emqx_limiter, brutal_purge, soft_purge, []}, - {suspend, [emqx_connection, emqx_ws_connection]}, + {suspend, [esockd_acceptor,emqx_connection, emqx_ws_connection]}, {load_module, emqx_frame, brutal_purge, soft_purge, []}, {update, emqx_connection, {advanced, []}}, {update, emqx_ws_connection, {advanced, []}}, - {resume, [emqx_connection, emqx_ws_connection]} + {resume, [esockd_acceptor,emqx_connection, emqx_ws_connection]} ]}, {"4.2.1", [ {load_module, emqx_limiter, brutal_purge, soft_purge, []}, - {suspend, [emqx_connection, emqx_ws_connection]}, + {suspend, [esockd_acceptor, emqx_connection, emqx_ws_connection]}, {load_module, emqx_frame, brutal_purge, soft_purge, []}, {update, emqx_connection, {advanced, []}}, {update, emqx_ws_connection, {advanced, []}}, - {resume, [emqx_connection, emqx_ws_connection]} + {resume, [esockd_acceptor, emqx_connection, emqx_ws_connection]} ]}, {<<".*">>, []} ], @@ -29,19 +29,19 @@ {load_module, emqx_channel, brutal_purge, soft_purge, []}, {load_module, emqx_metrics, brutal_purge, soft_purge, []}, {load_module, emqx_limiter, brutal_purge, soft_purge, []}, - {suspend, [emqx_connection, emqx_ws_connection]}, + {suspend, [esockd_acceptor, emqx_connection, emqx_ws_connection]}, {load_module, emqx_frame, brutal_purge, soft_purge, []}, {update, emqx_connection, {advanced, []}}, {update, emqx_ws_connection, {advanced, []}}, - {resume, [emqx_connection, emqx_ws_connection]} + {resume, [esockd_acceptor, emqx_connection, emqx_ws_connection]} ]}, {"4.2.1", [ {load_module, emqx_limiter, brutal_purge, soft_purge, []}, - {suspend, [emqx_connection, emqx_ws_connection]}, + {suspend, [esockd_acceptor, emqx_connection, emqx_ws_connection]}, {load_module, emqx_frame, brutal_purge, soft_purge, []}, {update, emqx_connection, {advanced, []}}, {update, emqx_ws_connection, {advanced, []}}, - {resume, [emqx_connection, emqx_ws_connection]} + {resume, [esockd_acceptor, emqx_connection, emqx_ws_connection]} ]}, {<<".*">>, []} ] From 6c1129dc6afcc0bca2c9cbf984421a474300ac93 Mon Sep 17 00:00:00 2001 From: emqx-ci-robot <77955145+emqx-ci-robot@users.noreply.github.com> Date: Sat, 30 Jan 2021 10:30:18 +0800 Subject: [PATCH 24/26] Auto-pull-request-on-2021-01-29 (#4114) * fix(share sub): fix the issue that the number of subscriptions dropped to 0 during the picking subscriber and caused a crash * Connection Busy Alarms (#3992) feat(emqx_connection): improve port_busy alarm * fix(emqx_connection): tune the congestion alarm params * chore(deps): upgrade esockd to 5.7.5 * fix(appup): correct the appup file --- rebar.config | 2 +- src/emqx.appup.src | 38 +++++++- src/emqx_alarm.erl | 96 ++++++++++++-------- src/emqx_channel.erl | 44 +++++---- src/emqx_congestion.erl | 161 +++++++++++++++++++++++++++++++++ src/emqx_connection.erl | 81 ++++------------- src/emqx_os_mon.erl | 4 +- src/emqx_shared_sub.erl | 2 +- test/emqx_connection_SUITE.erl | 4 + 9 files changed, 303 insertions(+), 129 deletions(-) create mode 100644 src/emqx_congestion.erl diff --git a/rebar.config b/rebar.config index a7ae1409f..2f5e362c8 100644 --- a/rebar.config +++ b/rebar.config @@ -6,7 +6,7 @@ [{gproc, {git, "https://github.com/uwiger/gproc", {tag, "0.8.0"}}}, {jiffy, {git, "https://github.com/emqx/jiffy", {tag, "1.0.5"}}}, {cowboy, {git, "https://github.com/emqx/cowboy", {tag, "2.7.1"}}}, - {esockd, {git, "https://github.com/emqx/esockd", {tag, "5.7.4"}}}, + {esockd, {git, "https://github.com/emqx/esockd", {tag, "5.7.5"}}}, {ekka, {git, "https://github.com/emqx/ekka", {tag, "0.7.5"}}}, {gen_rpc, {git, "https://github.com/emqx/gen_rpc", {tag, "2.5.0"}}}, {cuttlefish, {git, "https://github.com/emqx/cuttlefish", {tag, "v3.0.0"}}} diff --git a/src/emqx.appup.src b/src/emqx.appup.src index ed4b0695f..7322f75f5 100644 --- a/src/emqx.appup.src +++ b/src/emqx.appup.src @@ -3,6 +3,7 @@ {VSN, [ {"4.2.0", [ + {add_module, emqx_congestion}, {load_module, emqx_alarm, brutal_purge, soft_purge, []}, {load_module, emqx_channel, brutal_purge, soft_purge, []}, {load_module, emqx_metrics, brutal_purge, soft_purge, []}, @@ -11,37 +12,68 @@ {load_module, emqx_frame, brutal_purge, soft_purge, []}, {update, emqx_connection, {advanced, []}}, {update, emqx_ws_connection, {advanced, []}}, + {load_module, emqx_os_mon, brutal_purge, soft_purge, []}, + {load_module, emqx_shared_sub, brutal_purge, soft_purge, []}, {resume, [esockd_acceptor,emqx_connection, emqx_ws_connection]} ]}, {"4.2.1", [ + {add_module, emqx_congestion}, + {load_module, emqx_alarm, brutal_purge, soft_purge, []}, + {load_module, emqx_channel, brutal_purge, soft_purge, []}, {load_module, emqx_limiter, brutal_purge, soft_purge, []}, {suspend, [esockd_acceptor, emqx_connection, emqx_ws_connection]}, {load_module, emqx_frame, brutal_purge, soft_purge, []}, {update, emqx_connection, {advanced, []}}, {update, emqx_ws_connection, {advanced, []}}, + {load_module, emqx_os_mon, brutal_purge, soft_purge, []}, + {load_module, emqx_shared_sub, brutal_purge, soft_purge, []}, {resume, [esockd_acceptor, emqx_connection, emqx_ws_connection]} ]}, + {<<"4.2.[23]">>, [ + {add_module, emqx_congestion}, + {load_module, emqx_alarm, brutal_purge, soft_purge, []}, + {load_module, emqx_channel, brutal_purge, soft_purge, []}, + {load_module, emqx_connection, brutal_purge, soft_purge, []}, + {load_module, emqx_os_mon, brutal_purge, soft_purge, []}, + {load_module, emqx_shared_sub, brutal_purge, soft_purge, []} + ]}, {<<".*">>, []} ], [ {"4.2.0", [ + {load_module, emqx_shared_sub, brutal_purge, soft_purge, []}, + {load_module, emqx_os_mon, brutal_purge, soft_purge, []}, {load_module, emqx_alarm, brutal_purge, soft_purge, []}, - {load_module, emqx_channel, brutal_purge, soft_purge, []}, {load_module, emqx_metrics, brutal_purge, soft_purge, []}, {load_module, emqx_limiter, brutal_purge, soft_purge, []}, {suspend, [esockd_acceptor, emqx_connection, emqx_ws_connection]}, {load_module, emqx_frame, brutal_purge, soft_purge, []}, {update, emqx_connection, {advanced, []}}, {update, emqx_ws_connection, {advanced, []}}, - {resume, [esockd_acceptor, emqx_connection, emqx_ws_connection]} + {load_module, emqx_channel, brutal_purge, soft_purge, []}, + {resume, [esockd_acceptor, emqx_connection, emqx_ws_connection]}, + {delete_module, emqx_congestion} ]}, {"4.2.1", [ + {load_module, emqx_shared_sub, brutal_purge, soft_purge, []}, + {load_module, emqx_os_mon, brutal_purge, soft_purge, []}, {load_module, emqx_limiter, brutal_purge, soft_purge, []}, {suspend, [esockd_acceptor, emqx_connection, emqx_ws_connection]}, + {load_module, emqx_channel, brutal_purge, soft_purge, []}, {load_module, emqx_frame, brutal_purge, soft_purge, []}, {update, emqx_connection, {advanced, []}}, {update, emqx_ws_connection, {advanced, []}}, - {resume, [esockd_acceptor, emqx_connection, emqx_ws_connection]} + {load_module, emqx_alarm, brutal_purge, soft_purge, []}, + {resume, [esockd_acceptor, emqx_connection, emqx_ws_connection]}, + {delete_module, emqx_congestion} + ]}, + {<<"4.2.[23]">>, [ + {load_module, emqx_shared_sub, brutal_purge, soft_purge, []}, + {load_module, emqx_os_mon, brutal_purge, soft_purge, []}, + {load_module, emqx_connection, brutal_purge, soft_purge, []}, + {load_module, emqx_channel, brutal_purge, soft_purge, []}, + {load_module, emqx_alarm, brutal_purge, soft_purge, []}, + {delete_module, emqx_congestion} ]}, {<<".*">>, []} ] diff --git a/src/emqx_alarm.erl b/src/emqx_alarm.erl index ec2cb4338..12c888e6c 100644 --- a/src/emqx_alarm.erl +++ b/src/emqx_alarm.erl @@ -37,6 +37,7 @@ -export([ activate/1 , activate/2 , deactivate/1 + , deactivate/2 , delete_all_deactivated_alarms/0 , get_alarms/0 , get_alarms/1 @@ -132,7 +133,10 @@ activate(Name, Details) -> gen_server:call(?MODULE, {activate_alarm, Name, Details}). deactivate(Name) -> - gen_server:call(?MODULE, {deactivate_alarm, Name}). + gen_server:call(?MODULE, {deactivate_alarm, Name, no_details}). + +deactivate(Name, Details) -> + gen_server:call(?MODULE, {deactivate_alarm, Name, Details}). delete_all_deactivated_alarms() -> gen_server:call(?MODULE, delete_all_deactivated_alarms). @@ -179,34 +183,13 @@ handle_call({activate_alarm, Name, Details}, _From, State = #state{actions = Act {reply, ok, State} end; -handle_call({deactivate_alarm, Name}, _From, State = #state{actions = Actions, - size_limit = SizeLimit}) -> +handle_call({deactivate_alarm, Name, Details}, _From, State = #state{ + actions = Actions, size_limit = SizeLimit}) -> case mnesia:dirty_read(?ACTIVATED_ALARM, Name) of [] -> {reply, {error, not_found}, State}; - [#activated_alarm{name = Name, - details = Details, - message = Message, - activate_at = ActivateAt}] -> - case SizeLimit > 0 andalso (mnesia:table_info(?DEACTIVATED_ALARM, size) >= SizeLimit) of - true -> - case mnesia:dirty_first(?DEACTIVATED_ALARM) of - '$end_of_table' -> - ok; - ActivateAt2 -> - mnesia:dirty_delete(?DEACTIVATED_ALARM, ActivateAt2) - end; - false -> - ok - end, - Alarm = #deactivated_alarm{activate_at = ActivateAt, - name = Name, - details = Details, - message = Message, - deactivate_at = erlang:system_time(microsecond)}, - mnesia:dirty_delete(?ACTIVATED_ALARM, Name), - mnesia:dirty_write(?DEACTIVATED_ALARM, Alarm), - do_actions(deactivate, Alarm, Actions), + [Alarm] -> + deactivate_alarm(Details, SizeLimit, Actions, Alarm), {reply, ok, State} end; @@ -254,18 +237,50 @@ code_change(_OldVsn, State, _Extra) -> %% Internal functions %%------------------------------------------------------------------------------ +deactivate_alarm(Details, SizeLimit, Actions, #activated_alarm{ + activate_at = ActivateAt, name = Name, details = Details0, + message = Msg0}) -> + case SizeLimit > 0 andalso + (mnesia:table_info(?DEACTIVATED_ALARM, size) >= SizeLimit) of + true -> + case mnesia:dirty_first(?DEACTIVATED_ALARM) of + '$end_of_table' -> ok; + ActivateAt2 -> + mnesia:dirty_delete(?DEACTIVATED_ALARM, ActivateAt2) + end; + false -> ok + end, + HistoryAlarm = make_deactivated_alarm(ActivateAt, Name, Details0, Msg0, + erlang:system_time(microsecond)), + DeActAlarm = make_deactivated_alarm(ActivateAt, Name, Details, + normalize_message(Name, Details), + erlang:system_time(microsecond)), + mnesia:dirty_write(?DEACTIVATED_ALARM, HistoryAlarm), + mnesia:dirty_delete(?ACTIVATED_ALARM, Name), + do_actions(deactivate, DeActAlarm, Actions). + +make_deactivated_alarm(ActivateAt, Name, Details, Message, DeActivateAt) -> + #deactivated_alarm{ + activate_at = ActivateAt, + name = Name, + details = Details, + message = Message, + deactivate_at = DeActivateAt}. + deactivate_all_alarms() -> - lists:foreach(fun(#activated_alarm{name = Name, - details = Details, - message = Message, - activate_at = ActivateAt}) -> - mnesia:dirty_write(?DEACTIVATED_ALARM, - #deactivated_alarm{activate_at = ActivateAt, - name = Name, - details = Details, - message = Message, - deactivate_at = erlang:system_time(microsecond)}) - end, ets:tab2list(?ACTIVATED_ALARM)), + lists:foreach( + fun(#activated_alarm{name = Name, + details = Details, + message = Message, + activate_at = ActivateAt}) -> + mnesia:dirty_write(?DEACTIVATED_ALARM, + #deactivated_alarm{ + activate_at = ActivateAt, + name = Name, + details = Details, + message = Message, + deactivate_at = erlang:system_time(microsecond)}) + end, ets:tab2list(?ACTIVATED_ALARM)), mnesia:clear_table(?ACTIVATED_ALARM). ensure_delete_timer(State = #state{validity_period = ValidityPeriod}) -> @@ -332,6 +347,8 @@ normalize(#deactivated_alarm{activate_at = ActivateAt, deactivate_at => DeactivateAt, activated => false}. +normalize_message(Name, no_details) -> + list_to_binary(io_lib:format("~p", [Name])); normalize_message(high_system_memory_usage, #{high_watermark := HighWatermark}) -> list_to_binary(io_lib:format("System memory usage is higher than ~p%", [HighWatermark])); normalize_message(high_process_memory_usage, #{high_watermark := HighWatermark}) -> @@ -344,8 +361,7 @@ normalize_message(partition, #{occurred := Node}) -> list_to_binary(io_lib:format("Partition occurs at node ~s", [Node])); normalize_message(<<"resource", _/binary>>, #{type := Type, id := ID}) -> list_to_binary(io_lib:format("Resource ~s(~s) is down", [Type, ID])); -normalize_message(<<"mqtt_conn/congested/", ClientId/binary>>, _) -> - list_to_binary(io_lib:format("MQTT connection for clientid '~s' is congested", [ClientId])); +normalize_message(<<"mqtt_conn/congested/", Info/binary>>, _) -> + list_to_binary(io_lib:format("MQTT connection congested: ~s", [Info])); normalize_message(_Name, _UnknownDetails) -> <<"Unknown alarm">>. - diff --git a/src/emqx_channel.erl b/src/emqx_channel.erl index d9356ccd7..f4c71d5d2 100644 --- a/src/emqx_channel.erl +++ b/src/emqx_channel.erl @@ -31,6 +31,7 @@ -export([ info/1 , info/2 + , set_conn_state/2 , stats/1 , caps/1 ]). @@ -87,7 +88,7 @@ pendings :: list() }). --opaque(channel() :: #channel{}). +-type(channel() :: #channel{}). -type(conn_state() :: idle | connecting | connected | disconnected). @@ -127,26 +128,26 @@ info(Keys, Channel) when is_list(Keys) -> [{Key, info(Key, Channel)} || Key <- Keys]; info(conninfo, #channel{conninfo = ConnInfo}) -> ConnInfo; -info(zone, #channel{clientinfo = #{zone := Zone}}) -> - Zone; -info(clientid, #channel{clientinfo = #{clientid := ClientId}}) -> - ClientId; -info(username, #channel{clientinfo = #{username := Username}}) -> - Username; -info(socktype, #channel{conninfo = #{socktype := SockType}}) -> - SockType; -info(peername, #channel{conninfo = #{peername := Peername}}) -> - Peername; -info(sockname, #channel{conninfo = #{sockname := Sockname}}) -> - Sockname; -info(proto_name, #channel{conninfo = #{proto_name := ProtoName}}) -> - ProtoName; -info(proto_ver, #channel{conninfo = #{proto_ver := ProtoVer}}) -> - ProtoVer; -info(connected_at, #channel{conninfo = #{connected_at := ConnectedAt}}) -> - ConnectedAt; +info(socktype, #channel{conninfo = ConnInfo}) -> + maps:get(socktype, ConnInfo, undefined); +info(peername, #channel{conninfo = ConnInfo}) -> + maps:get(peername, ConnInfo, undefined); +info(sockname, #channel{conninfo = ConnInfo}) -> + maps:get(sockname, ConnInfo, undefined); +info(proto_name, #channel{conninfo = ConnInfo}) -> + maps:get(proto_name, ConnInfo, undefined); +info(proto_ver, #channel{conninfo = ConnInfo}) -> + maps:get(proto_ver, ConnInfo, undefined); +info(connected_at, #channel{conninfo = ConnInfo}) -> + maps:get(connected_at, ConnInfo, undefined); info(clientinfo, #channel{clientinfo = ClientInfo}) -> ClientInfo; +info(zone, #channel{clientinfo = ClientInfo}) -> + maps:get(zone, ClientInfo, undefined); +info(clientid, #channel{clientinfo = ClientInfo}) -> + maps:get(clientid, ClientInfo, undefined); +info(username, #channel{clientinfo = ClientInfo}) -> + maps:get(username, ClientInfo, undefined); info(session, #channel{session = Session}) -> maybe_apply(fun emqx_session:info/1, Session); info(conn_state, #channel{conn_state = ConnState}) -> @@ -163,6 +164,9 @@ info(alias_maximum, #channel{alias_maximum = Limits}) -> Limits; info(timers, #channel{timers = Timers}) -> Timers. +set_conn_state(ConnState, Channel) -> + Channel#channel{conn_state = ConnState}. + %% TODO: Add more stats. -spec(stats(channel()) -> emqx_types:stats()). stats(#channel{session = Session})-> @@ -1290,7 +1294,7 @@ packing_alias(Packet = #mqtt_packet{ }, Channel = ?IS_MQTT_V5 = #channel{topic_aliases = TopicAliases, alias_maximum = Limits}) -> case find_alias(outbound, Topic, TopicAliases) of - {ok, AliasId} -> + {ok, AliasId} -> NPublish = Publish#mqtt_packet_publish{ topic_name = <<>>, properties = maps:merge(Prop, #{'Topic-Alias' => AliasId}) diff --git a/src/emqx_congestion.erl b/src/emqx_congestion.erl new file mode 100644 index 000000000..36dc7ac9f --- /dev/null +++ b/src/emqx_congestion.erl @@ -0,0 +1,161 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_congestion). + +-export([ maybe_alarm_port_busy/3 + , maybe_alarm_port_busy/4 + , maybe_alarm_too_many_publish/5 + , maybe_alarm_too_many_publish/6 + , cancel_alarms/3 + ]). + +-define(ALARM_CONN_CONGEST(Channel, Reason), + list_to_binary(io_lib:format("mqtt_conn/congested/~s/~s/~s", [emqx_channel:info(clientid, Channel), + maps:get(username, emqx_channel:info(clientinfo, Channel), <<"undefined">>), + Reason]))). + +-define(ALARM_CONN_INFO_KEYS, [ + socktype, sockname, peername, clientid, username, proto_name, proto_ver, + connected_at, conn_state +]). +-define(ALARM_SOCK_STATS_KEYS, [send_pend, recv_cnt, recv_oct, send_cnt, send_oct]). +-define(ALARM_SOCK_OPTS_KEYS, [high_watermark, high_msgq_watermark, sndbuf, recbuf, buffer]). +-define(PROC_INFO_KEYS, [message_queue_len, memory, reductions]). +-define(ALARM_SENT(REASON), {alarm_sent, REASON}). +-define(ALL_ALARM_REASONS, [port_busy, too_many_publish]). +-define(CONFIRM_CLEAR(REASON), {alarm_confirm_clear, REASON}). +-define(CONFIRM_CLEAR_INTERVAL, 10000). + +maybe_alarm_port_busy(Socket, Transport, Channel) -> + maybe_alarm_port_busy(Socket, Transport, Channel, false). + +maybe_alarm_port_busy(Socket, Transport, Channel, ForceClear) -> + case is_tcp_congested(Socket, Transport) of + true -> alarm_congestion(Socket, Transport, Channel, port_busy); + false -> cancel_alarm_congestion(Socket, Transport, Channel, port_busy, + ForceClear) + end. + +maybe_alarm_too_many_publish(Socket, Transport, Channel, PubMsgCount, + MaxBatchSize) -> + maybe_alarm_too_many_publish(Socket, Transport, Channel, PubMsgCount, + MaxBatchSize, false). + +maybe_alarm_too_many_publish(Socket, Transport, Channel, PubMsgCount, + PubMsgCount = _MaxBatchSize, _ForceClear) -> + %% we only alarm it when the process is "too busy" + alarm_congestion(Socket, Transport, Channel, too_many_publish); +maybe_alarm_too_many_publish(Socket, Transport, Channel, PubMsgCount, + _MaxBatchSize, ForceClear) when PubMsgCount == 0 -> + %% but we clear the alarm until it is really "idle", to avoid sending + %% alarms and clears too frequently + cancel_alarm_congestion(Socket, Transport, Channel, too_many_publish, + ForceClear); +maybe_alarm_too_many_publish(_Socket, _Transport, _Channel, _PubMsgCount, + _MaxBatchSize, _ForceClear) -> + ok. + +cancel_alarms(Socket, Transport, Channel) -> + lists:foreach(fun(Reason) -> + do_cancel_alarm_congestion(Socket, Transport, Channel, Reason) + end, ?ALL_ALARM_REASONS). + +alarm_congestion(Socket, Transport, Channel, Reason) -> + case has_alarm_sent(Reason) of + false -> do_alarm_congestion(Socket, Transport, Channel, Reason); + true -> + %% pretend we have sent an alarm again + update_alarm_sent_at(Reason) + end. + +cancel_alarm_congestion(Socket, Transport, Channel, Reason, ForceClear) -> + case is_alarm_allowed_clear(Reason, ForceClear) of + true -> do_cancel_alarm_congestion(Socket, Transport, Channel, Reason); + false -> ok + end. + +do_alarm_congestion(Socket, Transport, Channel, Reason) -> + ok = update_alarm_sent_at(Reason), + AlarmDetails = tcp_congestion_alarm_details(Socket, Transport, Channel), + emqx_alarm:activate(?ALARM_CONN_CONGEST(Channel, Reason), AlarmDetails), + ok. + +do_cancel_alarm_congestion(Socket, Transport, Channel, Reason) -> + ok = remove_alarm_sent_at(Reason), + AlarmDetails = tcp_congestion_alarm_details(Socket, Transport, Channel), + emqx_alarm:deactivate(?ALARM_CONN_CONGEST(Channel, Reason), AlarmDetails), + ok. + +is_tcp_congested(Socket, Transport) -> + case Transport:getstat(Socket, [send_pend]) of + {ok, [{send_pend, N}]} when N > 0 -> true; + _ -> false + end. + +has_alarm_sent(Reason) -> + case get_alarm_sent_at(Reason) of + 0 -> false; + _ -> true + end. +update_alarm_sent_at(Reason) -> + erlang:put(?ALARM_SENT(Reason), timenow()), + ok. +remove_alarm_sent_at(Reason) -> + erlang:erase(?ALARM_SENT(Reason)), + ok. +get_alarm_sent_at(Reason) -> + case erlang:get(?ALARM_SENT(Reason)) of + undefined -> 0; + LastSentAt -> LastSentAt + end. + +is_alarm_allowed_clear(Reason, _ForceClear = true) -> + has_alarm_sent(Reason); +is_alarm_allowed_clear(Reason, _ForceClear = false) -> + %% only sent clears when the alarm was not triggered in the last + %% ?CONFIRM_CLEAR_INTERVAL time + case timenow() - get_alarm_sent_at(Reason) of + Elapse when Elapse >= ?CONFIRM_CLEAR_INTERVAL -> true; + _ -> false + end. + +timenow() -> + erlang:system_time(millisecond). + +%%============================================================================== +%% Alarm message +%%============================================================================== +tcp_congestion_alarm_details(Socket, Transport, Channel) -> + ProcInfo = process_info(self(), ?PROC_INFO_KEYS), + BasicInfo = [{pid, list_to_binary(pid_to_list(self()))} | ProcInfo], + Stat = case Transport:getstat(Socket, ?ALARM_SOCK_STATS_KEYS) of + {ok, Stat0} -> Stat0; + {error, _} -> [] + end, + Opts = case Transport:getopts(Socket, ?ALARM_SOCK_OPTS_KEYS) of + {ok, Opts0} -> Opts0; + {error, _} -> [] + end, + SockInfo = Stat ++ Opts, + ConnInfo = [conn_info(Key, Channel) || Key <- ?ALARM_CONN_INFO_KEYS], + maps:from_list(BasicInfo ++ ConnInfo ++ SockInfo). + +conn_info(Key, Channel) when Key =:= sockname; Key =:= peername -> + {IPStr, Port} = emqx_channel:info(Key, Channel), + {Key, iolist_to_binary([inet:ntoa(IPStr),":",integer_to_list(Port)])}; +conn_info(Key, Channel) -> + {Key, emqx_channel:info(Key, Channel)}. diff --git a/src/emqx_connection.erl b/src/emqx_connection.erl index 6e8ed9a46..91088f027 100644 --- a/src/emqx_connection.erl +++ b/src/emqx_connection.erl @@ -103,17 +103,6 @@ -define(ENABLED(X), (X =/= undefined)). --define(ALARM_TCP_CONGEST(Channel), - list_to_binary(io_lib:format("mqtt_conn/congested/~s/~s", [emqx_channel:info(clientid, Channel), maps:get(username, emqx_channel:info(clientinfo, Channel), <<"undefined">>)]))). - - --define(ALARM_CONN_INFO_KEYS, [ - socktype, sockname, peername, - clientid, username, proto_name, proto_ver, connected_at -]). --define(ALARM_SOCK_STATS_KEYS, [send_pend, recv_cnt, recv_oct, send_cnt, send_oct]). --define(ALARM_SOCK_OPTS_KEYS, [high_watermark, high_msgq_watermark, sndbuf, recbuf, buffer]). - -dialyzer({no_match, [info/2]}). -dialyzer({nowarn_function, [ init/4 , init_state/3 @@ -272,7 +261,7 @@ recvloop(Parent, State = #state{idle_timeout = IdleTimeout}) -> Msg -> process_msg([Msg], Parent, ensure_stats_timer(IdleTimeout, State)) after - IdleTimeout -> + IdleTimeout + 100 -> hibernate(Parent, cancel_stats_timer(State)) end. @@ -385,8 +374,12 @@ handle_msg({Passive, _Sock}, State) handle_info(activate_socket, NState1); handle_msg(Deliver = {deliver, _Topic, _Msg}, - State = #state{active_n = ActiveN}) -> - Delivers = [Deliver|emqx_misc:drain_deliver(ActiveN)], + #state{active_n = MaxBatchSize, transport = Transport, + socket = Socket, channel = Channel} = State) -> + Delivers0 = emqx_misc:drain_deliver(MaxBatchSize), + emqx_congestion:maybe_alarm_too_many_publish(Socket, Transport, Channel, + length(Delivers0), MaxBatchSize), + Delivers = [Deliver|Delivers0], with_channel(handle_deliver, [Delivers], State); %% Something sent @@ -438,10 +431,12 @@ handle_msg(Msg, State) -> %%-------------------------------------------------------------------- %% Terminate -terminate(Reason, State = #state{channel = Channel}) -> +terminate(Reason, State = #state{channel = Channel, transport = Transport, + socket = Socket}) -> ?LOG(debug, "Terminated due to ~p", [Reason]), - emqx_alarm:deactivate(?ALARM_TCP_CONGEST(Channel)), - emqx_channel:terminate(Reason, Channel), + Channel1 = emqx_channel:set_conn_state(disconnected, Channel), + emqx_congestion:cancel_alarms(Socket, Transport, Channel1), + emqx_channel:terminate(Reason, Channel1), close_socket(State), exit(Reason). @@ -553,8 +548,12 @@ handle_timeout(_TRef, limit_timeout, State) -> }, handle_info(activate_socket, NState); -handle_timeout(_TRef, emit_stats, State = - #state{channel = Channel}) -> +handle_timeout(_TRef, emit_stats, State = #state{active_n = MaxBatchSize, + channel = Channel, transport = Transport, socket = Socket}) -> + {_, MsgQLen} = erlang:process_info(self(), message_queue_len), + emqx_congestion:maybe_alarm_port_busy(Socket, Transport, Channel, true), + emqx_congestion:maybe_alarm_too_many_publish(Socket, Transport, Channel, + MsgQLen, MaxBatchSize, true), ClientId = emqx_channel:info(clientid, Channel), emqx_cm:set_chan_stats(ClientId, stats(State)), {ok, State#state{stats_timer = undefined}}; @@ -667,7 +666,7 @@ send(IoData, #state{transport = Transport, socket = Socket, channel = Channel}) Oct = iolist_size(IoData), ok = emqx_metrics:inc('bytes.sent', Oct), emqx_pd:inc_counter(outgoing_bytes, Oct), - maybe_warn_congestion(Socket, Transport, Channel), + emqx_congestion:maybe_alarm_port_busy(Socket, Transport, Channel), case Transport:async_send(Socket, IoData, [nosuspend]) of ok -> ok; Error = {error, _Reason} -> @@ -676,48 +675,6 @@ send(IoData, #state{transport = Transport, socket = Socket, channel = Channel}) ok end. -maybe_warn_congestion(Socket, Transport, Channel) -> - IsCongestAlarmSet = is_congestion_alarm_set(), - case is_congested(Socket, Transport) of - true when not IsCongestAlarmSet -> - ok = set_congestion_alarm(), - emqx_alarm:activate(?ALARM_TCP_CONGEST(Channel), - tcp_congestion_alarm_details(Socket, Transport, Channel)); - false when IsCongestAlarmSet -> - ok = clear_congestion_alarm(), - emqx_alarm:deactivate(?ALARM_TCP_CONGEST(Channel)); - _ -> ok - end. - -is_congested(Socket, Transport) -> - case Transport:getstat(Socket, [send_pend]) of - {ok, [{send_pend, N}]} when N > 0 -> true; - _ -> false - end. - -is_congestion_alarm_set() -> - case erlang:get(conn_congested) of - true -> true; - _ -> false - end. -set_congestion_alarm() -> - erlang:put(conn_congested, true), ok. -clear_congestion_alarm() -> - erlang:put(conn_congested, false), ok. - -tcp_congestion_alarm_details(Socket, Transport, Channel) -> - {ok, Stat} = Transport:getstat(Socket, ?ALARM_SOCK_STATS_KEYS), - {ok, Opts} = Transport:getopts(Socket, ?ALARM_SOCK_OPTS_KEYS), - SockInfo = maps:from_list(Stat ++ Opts), - ConnInfo = maps:from_list([conn_info(Key, Channel) || Key <- ?ALARM_CONN_INFO_KEYS]), - maps:merge(ConnInfo, SockInfo). - -conn_info(Key, Channel) when Key =:= sockname; Key =:= peername -> - {IPStr, Port} = emqx_channel:info(Key, Channel), - {Key, iolist_to_binary([inet:ntoa(IPStr),":",integer_to_list(Port)])}; -conn_info(Key, Channel) -> - {Key, emqx_channel:info(Key, Channel)}. - %%-------------------------------------------------------------------- %% Handle Info diff --git a/src/emqx_os_mon.erl b/src/emqx_os_mon.erl index 49517d8a6..2852c917b 100644 --- a/src/emqx_os_mon.erl +++ b/src/emqx_os_mon.erl @@ -145,12 +145,12 @@ handle_info({timeout, Timer, check}, State = #{timer := Timer, case emqx_vm:cpu_util() of %% TODO: should be improved? 0 -> State#{timer := undefined}; - Busy when Busy / 100 >= CPUHighWatermark -> + Busy when Busy >= CPUHighWatermark -> emqx_alarm:activate(high_cpu_usage, #{usage => Busy, high_watermark => CPUHighWatermark, low_watermark => CPULowWatermark}), ensure_check_timer(State); - Busy when Busy / 100 =< CPULowWatermark -> + Busy when Busy =< CPULowWatermark -> emqx_alarm:deactivate(high_cpu_usage), ensure_check_timer(State); _Busy -> diff --git a/src/emqx_shared_sub.erl b/src/emqx_shared_sub.erl index 71372cd90..17476199b 100644 --- a/src/emqx_shared_sub.erl +++ b/src/emqx_shared_sub.erl @@ -246,7 +246,7 @@ pick(Strategy, ClientId, Group, Topic, FailedSubs) -> do_pick(Strategy, ClientId, Group, Topic, FailedSubs) -> All = subscribers(Group, Topic), case All -- FailedSubs of - [] when FailedSubs =:= [] -> + [] when All =:= [] -> %% Genuinely no subscriber false; [] -> diff --git a/test/emqx_connection_SUITE.erl b/test/emqx_connection_SUITE.erl index 2538aeecb..d360bb5ae 100644 --- a/test/emqx_connection_SUITE.erl +++ b/test/emqx_connection_SUITE.erl @@ -54,6 +54,7 @@ init_per_suite(Config) -> ok = meck:expect(emqx_alarm, activate, fun(_, _) -> ok end), ok = meck:expect(emqx_alarm, deactivate, fun(_) -> ok end), + ok = meck:expect(emqx_alarm, deactivate, fun(_, _) -> ok end), Config. @@ -77,6 +78,9 @@ init_per_testcase(_TestCase, Config) -> (peercert, [sock]) -> undefined end), ok = meck:expect(emqx_transport, setopts, fun(_Sock, _Opts) -> ok end), + ok = meck:expect(emqx_transport, getopts, fun(_Sock, Options) -> + {ok, [{K, 0} || K <- Options]} + end), ok = meck:expect(emqx_transport, getstat, fun(_Sock, Options) -> {ok, [{K, 0} || K <- Options]} end), From 5ebf94a50a5d828822e38a226b4a3157007052f5 Mon Sep 17 00:00:00 2001 From: Zaiming Shi Date: Tue, 9 Feb 2021 09:19:08 +0100 Subject: [PATCH 25/26] chore(config): One config emqx.conf was split into small files for enterprise which leads to merge difficulties This commit merges back config into the same file. --- etc/cluster.conf | 170 ---- etc/emqx.conf | 1936 +++++++++++++++++++++++++++++++++++++++++++- etc/listeners.conf | 940 --------------------- etc/logger.conf | 170 ---- etc/rpc.conf | 98 --- etc/sys_mon.conf | 148 ---- etc/zones.conf | 327 -------- priv/emqx.schema | 18 +- 8 files changed, 1930 insertions(+), 1877 deletions(-) delete mode 100644 etc/cluster.conf delete mode 100644 etc/listeners.conf delete mode 100644 etc/logger.conf delete mode 100644 etc/rpc.conf delete mode 100644 etc/sys_mon.conf delete mode 100644 etc/zones.conf diff --git a/etc/cluster.conf b/etc/cluster.conf deleted file mode 100644 index cd4d3d007..000000000 --- a/etc/cluster.conf +++ /dev/null @@ -1,170 +0,0 @@ -##-------------------------------------------------------------------- -## Cluster -##-------------------------------------------------------------------- - -## Cluster name. -## -## Value: String -cluster.name = emqxcl - -## Specify the erlang distributed protocol. -## -## Value: Enum -## - inet_tcp: the default; handles TCP streams with IPv4 addressing. -## - inet6_tcp: handles TCP with IPv6 addressing. -## - inet_tls: using TLS for Erlang Distribution. -## -## vm.args: -proto_dist inet_tcp -cluster.proto_dist = inet_tcp - -## Cluster auto-discovery strategy. -## -## Value: Enum -## - manual: Manual join command -## - static: Static node list -## - mcast: IP Multicast -## - dns: DNS A Record -## - etcd: etcd -## - k8s: Kubernetes -## -## Default: manual -cluster.discovery = manual - -## Enable cluster autoheal from network partition. -## -## Value: on | off -## -## Default: on -cluster.autoheal = on - -## Autoclean down node. A down node will be removed from the cluster -## if this value > 0. -## -## Value: Duration -## -h: hour, e.g. '2h' for 2 hours -## -m: minute, e.g. '5m' for 5 minutes -## -s: second, e.g. '30s' for 30 seconds -## -## Default: 5m -cluster.autoclean = 5m - -##-------------------------------------------------------------------- -## Cluster using static node list - -## Node list of the cluster. -## -## Value: String -## cluster.static.seeds = emqx1@127.0.0.1,emqx2@127.0.0.1 - -##-------------------------------------------------------------------- -## Cluster using IP Multicast. - -## IP Multicast Address. -## -## Value: IP Address -## cluster.mcast.addr = 239.192.0.1 - -## Multicast Ports. -## -## Value: Port List -## cluster.mcast.ports = 4369,4370 - -## Multicast Iface. -## -## Value: Iface Address -## -## Default: 0.0.0.0 -## cluster.mcast.iface = 0.0.0.0 - -## Multicast Ttl. -## -## Value: 0-255 -## cluster.mcast.ttl = 255 - -## Multicast loop. -## -## Value: on | off -## cluster.mcast.loop = on - -##-------------------------------------------------------------------- -## Cluster using DNS A records. - -## DNS name. -## -## Value: String -## cluster.dns.name = localhost - -## The App name is used to build 'node.name' with IP address. -## -## Value: String -## cluster.dns.app = emqx - -##-------------------------------------------------------------------- -## Cluster using etcd - -## Etcd server list, seperated by ','. -## -## Value: String -## cluster.etcd.server = http://127.0.0.1:2379 - -## The prefix helps build nodes path in etcd. Each node in the cluster -## will create a path in etcd: v2/keys/// -## -## Value: String -## cluster.etcd.prefix = emqxcl - -## The TTL for node's path in etcd. -## -## Value: Duration -## -## Default: 1m, 1 minute -## cluster.etcd.node_ttl = 1m - -## Path to a file containing the client's private PEM-encoded key. -## -## Value: File -## cluster.etcd.ssl.keyfile = {{ platform_etc_dir }}/certs/client-key.pem - -## The path to a file containing the client's certificate. -## -## Value: File -## cluster.etcd.ssl.certfile = {{ platform_etc_dir }}/certs/client.pem - -## Path to the file containing PEM-encoded CA certificates. The CA certificates -## are used during server authentication and when building the client certificate chain. -## -## Value: File -## cluster.etcd.ssl.cacertfile = {{ platform_etc_dir }}/certs/ca.pem - -##-------------------------------------------------------------------- -## Cluster using Kubernetes - -## Kubernetes API server list, seperated by ','. -## -## Value: String -## cluster.k8s.apiserver = http://10.110.111.204:8080 - -## The service name helps lookup EMQ nodes in the cluster. -## -## Value: String -## cluster.k8s.service_name = emqx - -## The address type is used to extract host from k8s service. -## -## Value: ip | dns | hostname -## cluster.k8s.address_type = ip - -## The app name helps build 'node.name'. -## -## Value: String -## cluster.k8s.app_name = emqx - -## The suffix added to dns and hostname get from k8s service -## -## Value: String -## cluster.k8s.suffix = pod.cluster.local - -## Kubernetes Namespace -## -## Value: String -## cluster.k8s.namespace = default \ No newline at end of file diff --git a/etc/emqx.conf b/etc/emqx.conf index 58cf05cbe..6e6ebd4cb 100644 --- a/etc/emqx.conf +++ b/etc/emqx.conf @@ -2,12 +2,176 @@ ## EMQ X Configuration R4.0 ##==================================================================== -include {{ platform_etc_dir }}/cluster.conf -include {{ platform_etc_dir }}/rpc.conf -include {{ platform_etc_dir }}/logger.conf -include {{ platform_etc_dir }}/zones.conf -include {{ platform_etc_dir }}/listeners.conf -include {{ platform_etc_dir }}/sys_mon.conf +##-------------------------------------------------------------------- +## Cluster +##-------------------------------------------------------------------- + +## Cluster name. +## +## Value: String +cluster.name = emqxcl + +## Specify the erlang distributed protocol. +## +## Value: Enum +## - inet_tcp: the default; handles TCP streams with IPv4 addressing. +## - inet6_tcp: handles TCP with IPv6 addressing. +## - inet_tls: using TLS for Erlang Distribution. +## +## vm.args: -proto_dist inet_tcp +cluster.proto_dist = inet_tcp + +## Cluster auto-discovery strategy. +## +## Value: Enum +## - manual: Manual join command +## - static: Static node list +## - mcast: IP Multicast +## - dns: DNS A Record +## - etcd: etcd +## - k8s: Kubernetes +## +## Default: manual +cluster.discovery = manual + +## Enable cluster autoheal from network partition. +## +## Value: on | off +## +## Default: on +cluster.autoheal = on + +## Autoclean down node. A down node will be removed from the cluster +## if this value > 0. +## +## Value: Duration +## -h: hour, e.g. '2h' for 2 hours +## -m: minute, e.g. '5m' for 5 minutes +## -s: second, e.g. '30s' for 30 seconds +## +## Default: 5m +cluster.autoclean = 5m + +##-------------------------------------------------------------------- +## Cluster using static node list + +## Node list of the cluster. +## +## Value: String +## cluster.static.seeds = emqx1@127.0.0.1,emqx2@127.0.0.1 + +##-------------------------------------------------------------------- +## Cluster using IP Multicast. + +## IP Multicast Address. +## +## Value: IP Address +## cluster.mcast.addr = 239.192.0.1 + +## Multicast Ports. +## +## Value: Port List +## cluster.mcast.ports = 4369,4370 + +## Multicast Iface. +## +## Value: Iface Address +## +## Default: 0.0.0.0 +## cluster.mcast.iface = 0.0.0.0 + +## Multicast Ttl. +## +## Value: 0-255 +## cluster.mcast.ttl = 255 + +## Multicast loop. +## +## Value: on | off +## cluster.mcast.loop = on + +##-------------------------------------------------------------------- +## Cluster using DNS A records. + +## DNS name. +## +## Value: String +## cluster.dns.name = localhost + +## The App name is used to build 'node.name' with IP address. +## +## Value: String +## cluster.dns.app = emqx + +##-------------------------------------------------------------------- +## Cluster using etcd + +## Etcd server list, seperated by ','. +## +## Value: String +## cluster.etcd.server = http://127.0.0.1:2379 + +## The prefix helps build nodes path in etcd. Each node in the cluster +## will create a path in etcd: v2/keys/// +## +## Value: String +## cluster.etcd.prefix = emqxcl + +## The TTL for node's path in etcd. +## +## Value: Duration +## +## Default: 1m, 1 minute +## cluster.etcd.node_ttl = 1m + +## Path to a file containing the client's private PEM-encoded key. +## +## Value: File +## cluster.etcd.ssl.keyfile = {{ platform_etc_dir }}/certs/client-key.pem + +## The path to a file containing the client's certificate. +## +## Value: File +## cluster.etcd.ssl.certfile = {{ platform_etc_dir }}/certs/client.pem + +## Path to the file containing PEM-encoded CA certificates. The CA certificates +## are used during server authentication and when building the client certificate chain. +## +## Value: File +## cluster.etcd.ssl.cacertfile = {{ platform_etc_dir }}/certs/ca.pem + +##-------------------------------------------------------------------- +## Cluster using Kubernetes + +## Kubernetes API server list, seperated by ','. +## +## Value: String +## cluster.k8s.apiserver = http://10.110.111.204:8080 + +## The service name helps lookup EMQ nodes in the cluster. +## +## Value: String +## cluster.k8s.service_name = emqx + +## The address type is used to extract host from k8s service. +## +## Value: ip | dns | hostname +## cluster.k8s.address_type = ip + +## The app name helps build 'node.name'. +## +## Value: String +## cluster.k8s.app_name = emqx + +## The suffix added to dns and hostname get from k8s service +## +## Value: String +## cluster.k8s.suffix = pod.cluster.local + +## Kubernetes Namespace +## +## Value: String +## cluster.k8s.namespace = default ##-------------------------------------------------------------------- ## Node @@ -137,6 +301,276 @@ node.crash_dump = {{ platform_log_dir }}/crash.dump node.dist_listen_min = 6369 node.dist_listen_max = 6369 +##-------------------------------------------------------------------- +## RPC +##-------------------------------------------------------------------- +## RPC Mode. +## +## Value: sync | async +rpc.mode = async + +## Max batch size of async RPC requests. +## +## Value: Integer +## Zero or negative value disables rpc batching. +## +## NOTE: RPC batch won't work when rpc.mode = sync +rpc.async_batch_size = 256 + +## RPC port discovery +## +## The strategy for discovering the RPC listening port of other nodes. +## +## Value: Enum +## - manual: discover ports by `tcp_server_port` and `tcp_client_port`. +## - stateless: discover ports in a stateless manner. +## If node name is `emqx@127.0.0.1`, where the `` is an integer, +## then the listening port will be `5370 + ` +## +## Defaults to `stateless`. +rpc.port_discovery = stateless + +## TCP server port for RPC. +## +## Only takes effect when `rpc.port_discovery` = `manual`. +## +## Value: Port [1024-65535] +#rpc.tcp_server_port = 5369 + +## TCP port for outgoing RPC connections. +## +## Only takes effect when `rpc.port_discovery` = `manual`. +## +## Value: Port [1024-65535] +#rpc.tcp_client_port = 5369 + +## Number of outgoing RPC connections. +## +## Value: Interger [1-256] +## Defaults to NumberOfCPUSchedulers / 2 +#rpc.tcp_client_num = 1 + +## RCP Client connect timeout. +## +## Value: Seconds +rpc.connect_timeout = 5s + +## TCP send timeout of RPC client and server. +## +## Value: Seconds +rpc.send_timeout = 5s + +## Authentication timeout +## +## Value: Seconds +rpc.authentication_timeout = 5s + +## Default receive timeout for call() functions +## +## Value: Seconds +rpc.call_receive_timeout = 15s + +## Socket idle keepalive. +## +## Value: Seconds +rpc.socket_keepalive_idle = 900s + +## TCP Keepalive probes interval. +## +## Value: Seconds +rpc.socket_keepalive_interval = 75s + +## Probes lost to close the connection +## +## Value: Integer +rpc.socket_keepalive_count = 9 + +## Size of TCP send buffer. +## +## Value: Bytes +rpc.socket_sndbuf = 1MB + +## Size of TCP receive buffer. +## +## Value: Seconds +rpc.socket_recbuf = 1MB + +## Size of user-level software socket buffer. +## +## Value: Seconds +rpc.socket_buffer = 1MB + +##-------------------------------------------------------------------- +## Log +##-------------------------------------------------------------------- + +## Where to emit the logs. +## Enable the console (standard output) logs. +## +## Value: off | file | console | both +## - off: disable logs entirely +## - file: write logs only to file +## - console: write logs only to standard I/O +## - both: write logs both to file and standard I/O +log.to = both + +## The log severity level. +## +## Value: debug | info | notice | warning | error | critical | alert | emergency +## +## Note: Only the messages with severity level higher than or equal to +## this level will be logged. +## +## Default: warning +log.level = warning + +## The dir for log files. +## +## Value: Folder +log.dir = {{ platform_log_dir }} + +## The log filename for logs of level specified in "log.level". +## +## If `log.rotation` is enabled, this is the base name of the +## files. Each file in a rotated log is named .N, where N is an integer. +## +## Value: String +## Default: emqx.log +log.file = emqx.log + +## Limits the total number of characters printed for each log event. +## +## Value: Integer +## Default: No Limit +#log.chars_limit = 8192 + +## Enables the log rotation. +## With this enabled, new log files will be created when the current +## log file is full, max to `log.rotation.size` files will be created. +## +## Value: on | off +## Default: on +log.rotation = on + +## Maximum size of each log file. +## +## Value: Number +## Default: 10M +## Supported Unit: KB | MB | GB +log.rotation.size = 10MB + +## Maximum rotation count of log files. +## +## Value: Number +## Default: 5 +log.rotation.count = 5 + +## To create additional log files for specific log levels. +## +## Value: File Name +## Format: log.$level.file = $filename, +## where "$level" can be one of: debug, info, notice, warning, +## error, critical, alert, emergency +## Note: Log files for a specific log level will only contain all the logs +## that higher than or equal to that level +## +#log.info.file = info.log +#log.error.file = error.log + +## The max allowed queue length before switching to sync mode. +## +## Log overload protection parameter. If the message queue grows +## larger than this value the handler switches from anync to sync mode. +## +## Default: 100 +## +#log.sync_mode_qlen = 100 + +## The max allowed queue length before switching to drop mode. +## +## Log overload protection parameter. When the message queue grows +## larger than this threshold, the handler switches to a mode in which +## it drops all new events that senders want to log. +## +## Default: 3000 +## +#log.drop_mode_qlen = 3000 + +## The max allowed queue length before switching to flush mode. +## +## Log overload protection parameter. If the length of the message queue +## grows larger than this threshold, a flush (delete) operation takes place. +## To flush events, the handler discards the messages in the message queue +## by receiving them in a loop without logging. +## +## Default: 8000 +## +#log.flush_qlen = 8000 + +## Kill the log handler when it gets overloaded. +## +## Log overload protection parameter. It is possible that a handler, +## even if it can successfully manage peaks of high load without crashing, +## can build up a large message queue, or use a large amount of memory. +## We could kill the log handler in these cases and restart it after a +## few seconds. +## +## Default: on +## +#log.overload_kill = on + +## The max allowed queue length before killing the log hanlder. +## +## Log overload protection parameter. This is the maximum allowed queue +## length. If the message queue grows larger than this, the handler +## process is terminated. +## +## Default: 20000 +## +#log.overload_kill_qlen = 20000 + +## The max allowed memory size before killing the log hanlder. +## +## Log overload protection parameter. This is the maximum memory size +## that the handler process is allowed to use. If the handler grows +## larger than this, the process is terminated. +## +## Default: 30MB +## +#log.overload_kill_mem_size = 30MB + +## Restart the log hanlder after some seconds. +## +## Log overload protection parameter. If the handler is terminated, +## it restarts automatically after a delay specified in seconds. +## The value "infinity" prevents restarts. +## +## Default: 5s +## +#log.overload_kill_restart_after = 5s + +## Max burst count and time window for burst control. +## +## Log overload protection parameter. Large bursts of log events - many +## events received by the handler under a short period of time - can +## potentially cause problems. By specifying the maximum number of events +## to be handled within a certain time frame, the handler can avoid +## choking the log with massive amounts of printouts. +## +## This config controls the maximum number of events to handle within +## a time frame. After the limit is reached, successive events are +## dropped until the end of the time frame. +## +## Note that there would be no warning if any messages were +## dropped because of burst control. +## +## Comment this config out to disable the burst control feature. +## +## Value: MaxBurstCount,TimeWindow +## Default: disabled +## +#log.burst_limit = 20000, 1s + ##-------------------------------------------------------------------- ## Authentication/Access Control ##-------------------------------------------------------------------- @@ -152,6 +586,11 @@ allow_anonymous = true ## Value: allow | deny acl_nomatch = allow +## Default ACL File. +## +## Value: File Name +acl_file = {{ platform_etc_dir }}/acl.conf + ## Whether to enable ACL cache. ## ## If enabled, ACLs roles for each client will be cached in the memory @@ -242,10 +681,1334 @@ mqtt.ignore_loop_deliver = false mqtt.strict_mode = false ## Specify the response information returned to the client -## +## ## Value: String ## mqtt.response_information = example +##-------------------------------------------------------------------- +## Zones +##-------------------------------------------------------------------- + +##-------------------------------------------------------------------- +## External Zone + +## Idle timeout of the external MQTT connections. +## +## Value: duration +zone.external.idle_timeout = 15s + +## Enable ACL check. +## +## Value: Flag +zone.external.enable_acl = on + +## Enable ban check. +## +## Value: Flag +zone.external.enable_ban = on + +## Enable per connection statistics. +## +## Value: on | off +zone.external.enable_stats = on + +## The action when acl check reject current operation +## +## Value: ignore | disconnect +## Default: ignore +zone.external.acl_deny_action = ignore + +## Force the MQTT connection process GC after this number of +## messages | bytes passed through. +## +## Numbers delimited by `|'. Zero or negative is to disable. +zone.external.force_gc_policy = 16000|16MB + +## Max message queue length and total heap size to force shutdown +## connection/session process. +## Message queue here is the Erlang process mailbox, but not the number +## of queued MQTT messages of QoS 1 and 2. +## +## Numbers delimited by `|'. Zero or negative is to disable. +zone.external.force_shutdown_policy = 10000|32MB + +## Maximum MQTT packet size allowed. +## +## Value: Bytes +## Default: 1MB +## zone.external.max_packet_size = 64KB + +## Maximum length of MQTT clientId allowed. +## +## Value: Number [23-65535] +## zone.external.max_clientid_len = 1024 + +## Maximum topic levels allowed. 0 means no limit. +## +## Value: Number +## zone.external.max_topic_levels = 7 + +## Maximum QoS allowed. +## +## Value: 0 | 1 | 2 +## zone.external.max_qos_allowed = 2 + +## Maximum Topic Alias, 0 means no limit. +## +## Value: 0-65535 +## zone.external.max_topic_alias = 65535 + +## Whether the Server supports retained messages. +## +## Value: boolean +## zone.external.retain_available = true + +## Whether the Server supports Wildcard Subscriptions +## +## Value: boolean +## zone.external.wildcard_subscription = false + +## Whether the Server supports Shared Subscriptions +## +## Value: boolean +## zone.external.shared_subscription = false + +## Server Keep Alive +## +## Value: Number +## zone.external.server_keepalive = 0 + +## The backoff for MQTT keepalive timeout. The broker will kick a connection out +## until 'Keepalive * backoff * 2' timeout. +## +## Value: Float > 0.5 +zone.external.keepalive_backoff = 0.75 + +## Maximum number of subscriptions allowed, 0 means no limit. +## +## Value: Number +zone.external.max_subscriptions = 0 + +## Force to upgrade QoS according to subscription. +## +## Value: on | off +zone.external.upgrade_qos = off + +## Maximum size of the Inflight Window storing QoS1/2 messages delivered but unacked. +## +## Value: Number +zone.external.max_inflight = 32 + +## Retry interval for QoS1/2 message delivering. +## +## Value: Duration +zone.external.retry_interval = 30s + +## Maximum QoS2 packets (Client -> Broker) awaiting PUBREL, 0 means no limit. +## +## Value: Number +zone.external.max_awaiting_rel = 100 + +## The QoS2 messages (Client -> Broker) will be dropped if awaiting PUBREL timeout. +## +## Value: Duration +zone.external.await_rel_timeout = 300s + +## Default session expiry interval for MQTT V3.1.1 connections. +## +## Value: Duration +## -d: day +## -h: hour +## -m: minute +## -s: second +## +## Default: 2h, 2 hours +zone.external.session_expiry_interval = 2h + +## Maximum queue length. Enqueued messages when persistent client disconnected, +## or inflight window is full. 0 means no limit. +## +## Value: Number >= 0 +zone.external.max_mqueue_len = 1000 + +## Topic priorities. +## 'none' to indicate no priority table (by default), hence all messages +## are treated equal +## +## Priority number [1-255] +## Example: topic/1=10,topic/2=8 +## NOTE: comma and equal signs are not allowed for priority topic names +## NOTE: messages for topics not in the priority table are treated as +## either highest or lowest priority depending on the configured +## value for mqueue_default_priority +## +zone.external.mqueue_priorities = none + +## Default to highest priority for topics not matching priority table +## +## Value: highest | lowest +zone.external.mqueue_default_priority = highest + +## Whether to enqueue QoS0 messages. +## +## Value: false | true +zone.external.mqueue_store_qos0 = true + +## Whether to turn on flapping detect +## +## Value: on | off +zone.external.enable_flapping_detect = off + +## Message limit for the a external MQTT connection. +## +## Value: Number,Duration +## Example: 100 messages per 10 seconds. +#zone.external.rate_limit.conn_messages_in = 100,10s + +## Bytes limit for a external MQTT connections. +## +## Value: Number,Duration +## Example: 100KB incoming per 10 seconds. +#zone.external.rate_limit.conn_bytes_in = 100KB,10s + +## Messages quota for the each of external MQTT connection. +## This value consumed by the number of recipient on a message. +## +## Value: Number, Duration +## +## Example: 100 messaegs per 1s +#zone.external.quota.conn_messages_routing = 100,1s + +## Messages quota for the all of external MQTT connections. +## This value consumed by the number of recipient on a message. +## +## Value: Number, Duration +## +## Example: 200000 messaegs per 1s +#zone.external.quota.overall_messages_routing = 200000,1s + +## All the topics will be prefixed with the mountpoint path if this option is enabled. +## +## Variables in mountpoint path: +## - %c: clientid +## - %u: username +## +## Value: String +## zone.external.mountpoint = devicebound/ + +## Whether use username replace client id +## +## Value: boolean +## Default: false +zone.external.use_username_as_clientid = false + +## Whether to ignore loop delivery of messages.(for mqtt v3.1.1) +## +## Value: true | false +zone.external.ignore_loop_deliver = false + +## Whether to parse the MQTT frame in strict mode +## +## Value: true | false +zone.external.strict_mode = false + +## Specify the response information returned to the client +## +## Value: String +## zone.external.response_information = example + +##-------------------------------------------------------------------- +## Internal Zone + +zone.internal.allow_anonymous = true + +## Enable per connection stats. +## +## Value: Flag +zone.internal.enable_stats = on + +## Enable ACL check. +## +## Value: Flag +zone.internal.enable_acl = off + +## The action when acl check reject current operation +## +## Value: ignore | disconnect +## Default: ignore +zone.internal.acl_deny_action = ignore + +## See zone.$name.force_gc_policy +## zone.internal.force_gc_policy = 128000|128MB + +## See zone.$name.wildcard_subscription. +## +## Value: boolean +## zone.internal.wildcard_subscription = true + +## See zone.$name.shared_subscription. +## +## Value: boolean +## zone.internal.shared_subscription = true + +## See zone.$name.max_subscriptions. +## +## Value: Integer +zone.internal.max_subscriptions = 0 + +## See zone.$name.max_inflight +## +## Value: Number +zone.internal.max_inflight = 128 + +## See zone.$name.max_awaiting_rel +## +## Value: Number +zone.internal.max_awaiting_rel = 1000 + +## See zone.$name.max_mqueue_len +## +## Value: Number >= 0 +zone.internal.max_mqueue_len = 10000 + +## Whether to enqueue Qos0 messages. +## +## Value: false | true +zone.internal.mqueue_store_qos0 = true + +## Whether to turn on flapping detect +## +## Value: on | off +zone.internal.enable_flapping_detect = off + +## See zone.$name.force_shutdown_policy +zone.internal.force_shutdown_policy = 128000|28MB + +## All the topics will be prefixed with the mountpoint path if this option is enabled. +## +## Variables in mountpoint path: +## - %c: clientid +## - %u: username +## +## Value: String +## zone.internal.mountpoint = cloudbound/ + +## Whether to ignore loop delivery of messages.(for mqtt v3.1.1) +## +## Value: true | false +zone.internal.ignore_loop_deliver = false + +## Whether to parse the MQTT frame in strict mode +## +## Value: true | false +zone.internal.strict_mode = false + +## Specify the response information returned to the client +## +## Value: String +## zone.internal.response_information = example + +## Allow the zone's clients to bypass authentication step +## +## Value: true | false +zone.internal.bypass_auth_plugins = true + +##-------------------------------------------------------------------- +## Listeners +##-------------------------------------------------------------------- + +##-------------------------------------------------------------------- +## MQTT/TCP - External TCP Listener for MQTT Protocol + +## listener.tcp.$name is the IP address and port that the MQTT/TCP +## listener will bind. +## +## Value: IP:Port | Port +## +## Examples: 1883, 127.0.0.1:1883, ::1:1883 +listener.tcp.external = 0.0.0.0:1883 + +## The acceptor pool for external MQTT/TCP listener. +## +## Value: Number +listener.tcp.external.acceptors = 8 + +## Maximum number of concurrent MQTT/TCP connections. +## +## Value: Number +listener.tcp.external.max_connections = 1024000 + +## Maximum external connections per second. +## +## Value: Number +listener.tcp.external.max_conn_rate = 1000 + +## Specify the {active, N} option for the external MQTT/TCP Socket. +## +## Value: Number +listener.tcp.external.active_n = 100 + +## Zone of the external MQTT/TCP listener belonged to. +## +## See: zone.$name.* +## +## Value: String +listener.tcp.external.zone = external + +## The access control rules for the MQTT/TCP listener. +## +## See: https://github.com/emqtt/esockd#allowdeny +## +## Value: ACL Rule +## +## Example: allow 192.168.0.0/24 +listener.tcp.external.access.1 = allow all + +## Enable the Proxy Protocol V1/2 if the EMQ X cluster is deployed +## behind HAProxy or Nginx. +## +## See: https://www.haproxy.com/blog/haproxy/proxy-protocol/ +## +## Value: on | off +## listener.tcp.external.proxy_protocol = on + +## Sets the timeout for proxy protocol. EMQ X will close the TCP connection +## if no proxy protocol packet recevied within the timeout. +## +## Value: Duration +## listener.tcp.external.proxy_protocol_timeout = 3s + +## Enable the option for X.509 certificate based authentication. +## EMQX will use the common name of certificate as MQTT username. +## +## Value: cn | dn | crt +## listener.tcp.external.peer_cert_as_username = cn + +## The TCP backlog defines the maximum length that the queue of pending +## connections can grow to. +## +## Value: Number >= 0 +listener.tcp.external.backlog = 1024 + +## The TCP send timeout for external MQTT connections. +## +## Value: Duration +listener.tcp.external.send_timeout = 15s + +## Close the TCP connection if send timeout. +## +## Value: on | off +listener.tcp.external.send_timeout_close = on + +## The TCP receive buffer(os kernel) for MQTT connections. +## +## See: http://erlang.org/doc/man/inet.html +## +## Value: Bytes +## listener.tcp.external.recbuf = 2KB + +## The TCP send buffer(os kernel) for MQTT connections. +## +## See: http://erlang.org/doc/man/inet.html +## +## Value: Bytes +## listener.tcp.external.sndbuf = 2KB + +## The size of the user-level software buffer used by the driver. +## Not to be confused with options sndbuf and recbuf, which correspond +## to the Kernel socket buffers. It is recommended to have val(buffer) +## >= max(val(sndbuf),val(recbuf)) to avoid performance issues because +## of unnecessary copying. val(buffer) is automatically set to the above +## maximum when values sndbuf or recbuf are set. +## +## See: http://erlang.org/doc/man/inet.html +## +## Value: Bytes +## listener.tcp.external.buffer = 2KB + +## Sets the 'buffer = max(sndbuf, recbuf)' if this option is enabled. +## +## Value: on | off +## listener.tcp.external.tune_buffer = off + +## The socket is set to a busy state when the amount of data queued internally +## by the ERTS socket implementation reaches this limit. +## +## Value: on | off +## Defaults to 1MB +## listener.tcp.external.high_watermark = 1MB + +## The TCP_NODELAY flag for MQTT connections. Small amounts of data are +## sent immediately if the option is enabled. +## +## Value: true | false +listener.tcp.external.nodelay = true + +## The SO_REUSEADDR flag for TCP listener. +## +## Value: true | false +listener.tcp.external.reuseaddr = true + +##-------------------------------------------------------------------- +## Internal TCP Listener for MQTT Protocol + +## The IP address and port that the internal MQTT/TCP protocol listener +## will bind. +## +## Value: IP:Port, Port +## +## Examples: 11883, 127.0.0.1:11883, ::1:11883 +listener.tcp.internal = 127.0.0.1:11883 + +## The acceptor pool for internal MQTT/TCP listener. +## +## Value: Number +listener.tcp.internal.acceptors = 4 + +## Maximum number of concurrent MQTT/TCP connections. +## +## Value: Number +listener.tcp.internal.max_connections = 1024000 + +## Maximum internal connections per second. +## +## Value: Number +listener.tcp.internal.max_conn_rate = 1000 + +## Specify the {active, N} option for the internal MQTT/TCP Socket. +## +## Value: Number +listener.tcp.internal.active_n = 1000 + +## Zone of the internal MQTT/TCP listener belonged to. +## +## Value: String +listener.tcp.internal.zone = internal + +## The TCP backlog of internal MQTT/TCP Listener. +## +## See: listener.tcp.$name.backlog +## +## Value: Number >= 0 +listener.tcp.internal.backlog = 512 + +## The TCP send timeout for internal MQTT connections. +## +## See: listener.tcp.$name.send_timeout +## +## Value: Duration +listener.tcp.internal.send_timeout = 5s + +## Close the MQTT/TCP connection if send timeout. +## +## See: listener.tcp.$name.send_timeout_close +## +## Value: on | off +listener.tcp.internal.send_timeout_close = on + +## The TCP receive buffer(os kernel) for internal MQTT connections. +## +## See: listener.tcp.$name.recbuf +## +## Value: Bytes +listener.tcp.internal.recbuf = 64KB + +## The TCP send buffer(os kernel) for internal MQTT connections. +## +## See: http://erlang.org/doc/man/inet.html +## +## Value: Bytes +listener.tcp.internal.sndbuf = 64KB + +## The size of the user-level software buffer used by the driver. +## +## See: listener.tcp.$name.buffer +## +## Value: Bytes +## listener.tcp.internal.buffer = 16KB + +## Sets the 'buffer = max(sndbuf, recbuf)' if this option is enabled. +## +## See: listener.tcp.$name.tune_buffer +## +## Value: on | off +## listener.tcp.internal.tune_buffer = off + +## The TCP_NODELAY flag for internal MQTT connections. +## +## See: listener.tcp.$name.nodelay +## +## Value: true | false +listener.tcp.internal.nodelay = false + +## The SO_REUSEADDR flag for MQTT/TCP Listener. +## +## Value: true | false +listener.tcp.internal.reuseaddr = true + +##-------------------------------------------------------------------- +## MQTT/SSL - External SSL Listener for MQTT Protocol + +## listener.ssl.$name is the IP address and port that the MQTT/SSL +## listener will bind. +## +## Value: IP:Port | Port +## +## Examples: 8883, 127.0.0.1:8883, ::1:8883 +listener.ssl.external = 8883 + +## The acceptor pool for external MQTT/SSL listener. +## +## Value: Number +listener.ssl.external.acceptors = 16 + +## Maximum number of concurrent MQTT/SSL connections. +## +## Value: Number +listener.ssl.external.max_connections = 102400 + +## Maximum MQTT/SSL connections per second. +## +## Value: Number +listener.ssl.external.max_conn_rate = 500 + +## Specify the {active, N} option for the internal MQTT/SSL Socket. +## +## Value: Number +listener.ssl.external.active_n = 100 + +## Zone of the external MQTT/SSL listener belonged to. +## +## Value: String +listener.ssl.external.zone = external + +## The access control rules for the MQTT/SSL listener. +## +## See: listener.tcp.$name.access +## +## Value: ACL Rule +listener.ssl.external.access.1 = allow all + +## Enable the Proxy Protocol V1/2 if the EMQ cluster is deployed behind +## HAProxy or Nginx. +## +## See: listener.tcp.$name.proxy_protocol +## +## Value: on | off +## listener.ssl.external.proxy_protocol = on + +## Sets the timeout for proxy protocol. +## +## See: listener.tcp.$name.proxy_protocol_timeout +## +## Value: Duration +## listener.ssl.external.proxy_protocol_timeout = 3s + +## TLS versions only to protect from POODLE attack. +## +## See: http://erlang.org/doc/man/ssl.html +## +## Value: String, seperated by ',' +## listener.ssl.external.tls_versions = tlsv1.2,tlsv1.1,tlsv1 + +## TLS Handshake timeout. +## +## Value: Duration +listener.ssl.external.handshake_timeout = 15s + +## Maximum number of non-self-issued intermediate certificates that +## can follow the peer certificate in a valid certification path. +## +## Value: Number +## listener.ssl.external.depth = 10 + +## String containing the user's password. Only used if the private keyfile +## is password-protected. +## +## Value: String +## listener.ssl.external.key_password = yourpass + +## Path to the file containing the user's private PEM-encoded key. +## +## See: http://erlang.org/doc/man/ssl.html +## +## Value: File +listener.ssl.external.keyfile = {{ platform_etc_dir }}/certs/key.pem + +## Path to a file containing the user certificate. +## +## See: http://erlang.org/doc/man/ssl.html +## +## Value: File +listener.ssl.external.certfile = {{ platform_etc_dir }}/certs/cert.pem + +## Path to the file containing PEM-encoded CA certificates. The CA certificates +## are used during server authentication and when building the client certificate chain. +## +## Value: File +## listener.ssl.external.cacertfile = {{ platform_etc_dir }}/certs/cacert.pem + +## The Ephemeral Diffie-Helman key exchange is a very effective way of +## ensuring Forward Secrecy by exchanging a set of keys that never hit +## the wire. Since the DH key is effectively signed by the private key, +## it needs to be at least as strong as the private key. In addition, +## the default DH groups that most of the OpenSSL installations have +## are only a handful (since they are distributed with the OpenSSL +## package that has been built for the operating system it’s running on) +## and hence predictable (not to mention, 1024 bits only). +## In order to escape this situation, first we need to generate a fresh, +## strong DH group, store it in a file and then use the option above, +## to force our SSL application to use the new DH group. Fortunately, +## OpenSSL provides us with a tool to do that. Simply run: +## openssl dhparam -out dh-params.pem 2048 +## +## Value: File +## listener.ssl.external.dhfile = {{ platform_etc_dir }}/certs/dh-params.pem + +## A server only does x509-path validation in mode verify_peer, +## as it then sends a certificate request to the client (this +## message is not sent if the verify option is verify_none). +## You can then also want to specify option fail_if_no_peer_cert. +## More information at: http://erlang.org/doc/man/ssl.html +## +## Value: verify_peer | verify_none +## listener.ssl.external.verify = verify_peer + +## Used together with {verify, verify_peer} by an SSL server. If set to true, +## the server fails if the client does not have a certificate to send, that is, +## sends an empty certificate. +## +## Value: true | false +## listener.ssl.external.fail_if_no_peer_cert = true + +## This is the single most important configuration option of an Erlang SSL +## application. Ciphers (and their ordering) define the way the client and +## server encrypt information over the wire, from the initial Diffie-Helman +## key exchange, the session key encryption ## algorithm and the message +## digest algorithm. Selecting a good cipher suite is critical for the +## application’s data security, confidentiality and performance. +## +## The cipher list above offers: +## +## A good balance between compatibility with older browsers. +## It can get stricter for Machine-To-Machine scenarios. +## Perfect Forward Secrecy. +## No old/insecure encryption and HMAC algorithms +## +## Most of it was copied from Mozilla’s Server Side TLS article +## +## Value: Ciphers +listener.ssl.external.ciphers = ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES256-SHA384,ECDHE-RSA-AES256-SHA384,ECDHE-ECDSA-DES-CBC3-SHA,ECDH-ECDSA-AES256-GCM-SHA384,ECDH-RSA-AES256-GCM-SHA384,ECDH-ECDSA-AES256-SHA384,ECDH-RSA-AES256-SHA384,DHE-DSS-AES256-GCM-SHA384,DHE-DSS-AES256-SHA256,AES256-GCM-SHA384,AES256-SHA256,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES128-SHA256,ECDHE-RSA-AES128-SHA256,ECDH-ECDSA-AES128-GCM-SHA256,ECDH-RSA-AES128-GCM-SHA256,ECDH-ECDSA-AES128-SHA256,ECDH-RSA-AES128-SHA256,DHE-DSS-AES128-GCM-SHA256,DHE-DSS-AES128-SHA256,AES128-GCM-SHA256,AES128-SHA256,ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,DHE-DSS-AES256-SHA,ECDH-ECDSA-AES256-SHA,ECDH-RSA-AES256-SHA,AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,DHE-DSS-AES128-SHA,ECDH-ECDSA-AES128-SHA,ECDH-RSA-AES128-SHA,AES128-SHA + + +## Ciphers for TLS PSK. +## Note that 'listener.ssl.external.ciphers' and 'listener.ssl.external.psk_ciphers' cannot +## be configured at the same time. +## See 'https://tools.ietf.org/html/rfc4279#section-2'. +#listener.ssl.external.psk_ciphers = PSK-AES128-CBC-SHA,PSK-AES256-CBC-SHA,PSK-3DES-EDE-CBC-SHA,PSK-RC4-SHA + +## SSL parameter renegotiation is a feature that allows a client and a server +## to renegotiate the parameters of the SSL connection on the fly. +## RFC 5746 defines a more secure way of doing this. By enabling secure renegotiation, +## you drop support for the insecure renegotiation, prone to MitM attacks. +## +## Value: on | off +## listener.ssl.external.secure_renegotiate = off + +## A performance optimization setting, it allows clients to reuse +## pre-existing sessions, instead of initializing new ones. +## Read more about it here. +## +## See: http://erlang.org/doc/man/ssl.html +## +## Value: on | off +## listener.ssl.external.reuse_sessions = on + +## An important security setting, it forces the cipher to be set based +## on the server-specified order instead of the client-specified order, +## hence enforcing the (usually more properly configured) security +## ordering of the server administrator. +## +## Value: on | off +## listener.ssl.external.honor_cipher_order = on + +## Use the CN, DN or CRT field from the client certificate as a username. +## Notice that 'verify' should be set as 'verify_peer'. +## +## Value: cn | dn | crt +## listener.ssl.external.peer_cert_as_username = cn + +## TCP backlog for the SSL connection. +## +## See listener.tcp.$name.backlog +## +## Value: Number >= 0 +## listener.ssl.external.backlog = 1024 + +## The TCP send timeout for the SSL connection. +## +## See listener.tcp.$name.send_timeout +## +## Value: Duration +## listener.ssl.external.send_timeout = 15s + +## Close the SSL connection if send timeout. +## +## See: listener.tcp.$name.send_timeout_close +## +## Value: on | off +## listener.ssl.external.send_timeout_close = on + +## The TCP receive buffer(os kernel) for the SSL connections. +## +## See: listener.tcp.$name.recbuf +## +## Value: Bytes +## listener.ssl.external.recbuf = 4KB + +## The TCP send buffer(os kernel) for internal MQTT connections. +## +## See: listener.tcp.$name.sndbuf +## +## Value: Bytes +## listener.ssl.external.sndbuf = 4KB + +## The size of the user-level software buffer used by the driver. +## +## See: listener.tcp.$name.buffer +## +## Value: Bytes +## listener.ssl.external.buffer = 4KB + +## Sets the 'buffer = max(sndbuf, recbuf)' if this option is enabled. +## +## See: listener.tcp.$name.tune_buffer +## +## Value: on | off +## listener.ssl.external.tune_buffer = off + +## The TCP_NODELAY flag for SSL connections. +## +## See: listener.tcp.$name.nodelay +## +## Value: true | false +## listener.ssl.external.nodelay = true + +## The SO_REUSEADDR flag for MQTT/SSL Listener. +## +## Value: true | false +listener.ssl.external.reuseaddr = true + +##-------------------------------------------------------------------- +## External WebSocket listener for MQTT protocol + +## listener.ws.$name is the IP address and port that the MQTT/WebSocket +## listener will bind. +## +## Value: IP:Port | Port +## +## Examples: 8083, 127.0.0.1:8083, ::1:8083 +listener.ws.external = 8083 + +## The path of WebSocket MQTT endpoint +## +## Value: URL Path +listener.ws.external.mqtt_path = /mqtt + +## The acceptor pool for external MQTT/WebSocket listener. +## +## Value: Number +listener.ws.external.acceptors = 4 + +## Maximum number of concurrent MQTT/WebSocket connections. +## +## Value: Number +listener.ws.external.max_connections = 102400 + +## Maximum MQTT/WebSocket connections per second. +## +## Value: Number +listener.ws.external.max_conn_rate = 1000 + +## Simulate the {active, N} option for the MQTT/WebSocket connections. +## +## Value: Number +listener.ws.external.active_n = 100 + +## Zone of the external MQTT/WebSocket listener belonged to. +## +## Value: String +listener.ws.external.zone = external + +## The access control for the MQTT/WebSocket listener. +## +## See: listener.ws.$name.access +## +## Value: ACL Rule +listener.ws.external.access.1 = allow all + +## If set to true, the server fails if the client does not have a Sec-WebSocket-Protocol to send. +## Set to false for WeChat MiniApp. +## +## Value: true | false +## listener.ws.external.fail_if_no_subprotocol = on + +## Supported subprotocols +## +## Default: mqtt, mqtt-v3, mqtt-v3.1.1, mqtt-v5 +## listener.ws.external.supported_protocols = mqtt, mqtt-v3, mqtt-v3.1.1, mqtt-v5 + +## Enable the Proxy Protocol V1/2 if the EMQ cluster is deployed behind +## HAProxy or Nginx. +## +## See: listener.ws.$name.proxy_protocol +## +## Value: on | off +## listener.ws.external.proxy_protocol = on + +## Sets the timeout for proxy protocol. +## +## See: listener.ws.$name.proxy_protocol_timeout +## +## Value: Duration +## listener.ws.external.proxy_protocol_timeout = 3s + +## The TCP backlog of external MQTT/WebSocket Listener. +## +## See: listener.ws.$name.backlog +## +## Value: Number >= 0 +listener.ws.external.backlog = 1024 + +## The TCP send timeout for external MQTT/WebSocket connections. +## +## See: listener.ws.$name.send_timeout +## +## Value: Duration +listener.ws.external.send_timeout = 15s + +## Close the MQTT/WebSocket connection if send timeout. +## +## See: listener.ws.$name.send_timeout_close +## +## Value: on | off +listener.ws.external.send_timeout_close = on + +## The TCP receive buffer(os kernel) for external MQTT/WebSocket connections. +## +## See: listener.ws.$name.recbuf +## +## Value: Bytes +## listener.ws.external.recbuf = 2KB + +## The TCP send buffer(os kernel) for external MQTT/WebSocket connections. +## +## See: listener.ws.$name.sndbuf +## +## Value: Bytes +## listener.ws.external.sndbuf = 2KB + +## The size of the user-level software buffer used by the driver. +## +## See: listener.ws.$name.buffer +## +## Value: Bytes +## listener.ws.external.buffer = 2KB + +## Sets the 'buffer = max(sndbuf, recbuf)' if this option is enabled. +## +## See: listener.ws.$name.tune_buffer +## +## Value: on | off +## listener.ws.external.tune_buffer = off + +## The TCP_NODELAY flag for external MQTT/WebSocket connections. +## +## See: listener.ws.$name.nodelay +## +## Value: true | false +listener.ws.external.nodelay = true + +## The compress flag for external MQTT/WebSocket connections. +## +## If this Value is set true,the websocket message would be compressed +## +## Value: true | false +## listener.ws.external.compress = true + +## The level of deflate options for external MQTT/WebSocket connections. +## +## See: listener.ws.$name.deflate_opts.level +## +## Value: none | default | best_compression | best_speed +## listener.ws.external.deflate_opts.level = default + +## The mem_level of deflate options for external MQTT/WebSocket connections. +## +## See: listener.ws.$name.deflate_opts.mem_level +## +## Valid range is 1-9 +## listener.ws.external.deflate_opts.mem_level = 8 + +## The strategy of deflate options for external MQTT/WebSocket connections. +## +## See: listener.ws.$name.deflate_opts.strategy +## +## Value: default | filtered | huffman_only | rle +## listener.ws.external.deflate_opts.strategy = default + +## The deflate option for external MQTT/WebSocket connections. +## +## See: listener.ws.$name.deflate_opts.server_context_takeover +## +## Value: takeover | no_takeover +## listener.ws.external.deflate_opts.server_context_takeover = takeover + +## The deflate option for external MQTT/WebSocket connections. +## +## See: listener.ws.$name.deflate_opts.client_context_takeover +## +## Value: takeover | no_takeover +## listener.ws.external.deflate_opts.client_context_takeover = takeover + +## The deflate options for external MQTT/WebSocket connections. +## +## See: listener.ws.$name.deflate_opts.server_max_window_bits +## +## Valid range is 8-15 +## listener.ws.external.deflate_opts.server_max_window_bits = 15 + +## The deflate options for external MQTT/WebSocket connections. +## +## See: listener.ws.$name.deflate_opts.client_max_window_bits +## +## Valid range is 8-15 +## listener.ws.external.deflate_opts.client_max_window_bits = 15 + +## The idle timeout for external MQTT/WebSocket connections. +## +## See: listener.ws.$name.idle_timeout +## +## Value: Duration +## listener.ws.external.idle_timeout = 60s + +## The max frame size for external MQTT/WebSocket connections. +## +## +## Value: Number +## listener.ws.external.max_frame_size = 0 + +## Whether a WebSocket message is allowed to contain multiple MQTT packets +## +## Value: single | multiple +## listener.ws.external.mqtt_piggyback = multiple + +##-------------------------------------------------------------------- +## External WebSocket/SSL listener for MQTT Protocol + +## listener.wss.$name is the IP address and port that the MQTT/WebSocket/SSL +## listener will bind. +## +## Value: IP:Port | Port +## +## Examples: 8084, 127.0.0.1:8084, ::1:8084 +listener.wss.external = 8084 + +## The path of WebSocket MQTT endpoint +## +## Value: URL Path +listener.wss.external.mqtt_path = /mqtt + +## The acceptor pool for external MQTT/WebSocket/SSL listener. +## +## Value: Number +listener.wss.external.acceptors = 4 + +## Maximum number of concurrent MQTT/Webwocket/SSL connections. +## +## Value: Number +listener.wss.external.max_connections = 16 + +## Maximum MQTT/WebSocket/SSL connections per second. +## +## See: listener.tcp.$name.max_conn_rate +## +## Value: Number +listener.wss.external.max_conn_rate = 1000 + +## Simulate the {active, N} option for the MQTT/WebSocket/SSL connections. +## +## Value: Number +listener.wss.external.active_n = 100 + +## Zone of the external MQTT/WebSocket/SSL listener belonged to. +## +## Value: String +listener.wss.external.zone = external + +## The access control rules for the MQTT/WebSocket/SSL listener. +## +## See: listener.tcp.$name.access. +## +## Value: ACL Rule +listener.wss.external.access.1 = allow all + +## If set to true, the server fails if the client does not have a Sec-WebSocket-Protocol to send. +## Set to false for WeChat MiniApp. +## +## Value: true | false +## listener.wss.external.fail_if_no_subprotocol = true + +## Supported subprotocols +## +## Default: mqtt, mqtt-v3, mqtt-v3.1.1, mqtt-v5 +## listener.wss.external.supported_protocols = mqtt, mqtt-v3, mqtt-v3.1.1, mqtt-v5 + +## Enable the Proxy Protocol V1/2 support. +## +## See: listener.tcp.$name.proxy_protocol +## +## Value: on | off +## listener.wss.external.proxy_protocol = on + +## Sets the timeout for proxy protocol. +## +## See: listener.tcp.$name.proxy_protocol_timeout +## +## Value: Duration +## listener.wss.external.proxy_protocol_timeout = 3s + +## TLS versions only to protect from POODLE attack. +## +## See: listener.ssl.$name.tls_versions +## +## Value: String, seperated by ',' +## listener.wss.external.tls_versions = tlsv1.2,tlsv1.1,tlsv1 + +## Path to the file containing the user's private PEM-encoded key. +## +## See: listener.ssl.$name.keyfile +## +## Value: File +listener.wss.external.keyfile = {{ platform_etc_dir }}/certs/key.pem + +## Path to a file containing the user certificate. +## +## See: listener.ssl.$name.certfile +## +## Value: File +listener.wss.external.certfile = {{ platform_etc_dir }}/certs/cert.pem + +## Path to the file containing PEM-encoded CA certificates. +## +## See: listener.ssl.$name.cacert +## +## Value: File +## listener.wss.external.cacertfile = {{ platform_etc_dir }}/certs/cacert.pem + +## Maximum number of non-self-issued intermediate certificates that +## can follow the peer certificate in a valid certification path. +## +## See: listener.ssl.external.depth +## +## Value: Number +## listener.wss.external.depth = 10 + +## String containing the user's password. Only used if the private keyfile +## is password-protected. +## +## See: listener.ssl.$name.key_password +## +## Value: String +## listener.wss.external.key_password = yourpass + +## See: listener.ssl.$name.dhfile +## +## Value: File +## listener.ssl.external.dhfile = {{ platform_etc_dir }}/certs/dh-params.pem + +## See: listener.ssl.$name.verify +## +## Value: verify_peer | verify_none +## listener.wss.external.verify = verify_peer + +## See: listener.ssl.$name.fail_if_no_peer_cert +## +## Value: false | true +## listener.wss.external.fail_if_no_peer_cert = true + +## See: listener.ssl.$name.ciphers +## +## Value: Ciphers +listener.wss.external.ciphers = ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES256-SHA384,ECDHE-RSA-AES256-SHA384,ECDHE-ECDSA-DES-CBC3-SHA,ECDH-ECDSA-AES256-GCM-SHA384,ECDH-RSA-AES256-GCM-SHA384,ECDH-ECDSA-AES256-SHA384,ECDH-RSA-AES256-SHA384,DHE-DSS-AES256-GCM-SHA384,DHE-DSS-AES256-SHA256,AES256-GCM-SHA384,AES256-SHA256,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES128-SHA256,ECDHE-RSA-AES128-SHA256,ECDH-ECDSA-AES128-GCM-SHA256,ECDH-RSA-AES128-GCM-SHA256,ECDH-ECDSA-AES128-SHA256,ECDH-RSA-AES128-SHA256,DHE-DSS-AES128-GCM-SHA256,DHE-DSS-AES128-SHA256,AES128-GCM-SHA256,AES128-SHA256,ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,DHE-DSS-AES256-SHA,ECDH-ECDSA-AES256-SHA,ECDH-RSA-AES256-SHA,AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,DHE-DSS-AES128-SHA,ECDH-ECDSA-AES128-SHA,ECDH-RSA-AES128-SHA,AES128-SHA + +## Ciphers for TLS PSK. +## Note that 'listener.wss.external.ciphers' and 'listener.wss.external.psk_ciphers' cannot +## be configured at the same time. +## See 'https://tools.ietf.org/html/rfc4279#section-2'. +## listener.wss.external.psk_ciphers = PSK-AES128-CBC-SHA,PSK-AES256-CBC-SHA,PSK-3DES-EDE-CBC-SHA,PSK-RC4-SHA + +## See: listener.ssl.$name.secure_renegotiate +## +## Value: on | off +## listener.wss.external.secure_renegotiate = off + +## See: listener.ssl.$name.reuse_sessions +## +## Value: on | off +## listener.wss.external.reuse_sessions = on + +## See: listener.ssl.$name.honor_cipher_order +## +## Value: on | off +## listener.wss.external.honor_cipher_order = on + +## See: listener.ssl.$name.peer_cert_as_username +## +## Value: cn | dn | crt +## listener.wss.external.peer_cert_as_username = cn + +## TCP backlog for the WebSocket/SSL connection. +## +## See: listener.tcp.$name.backlog +## +## Value: Number >= 0 +listener.wss.external.backlog = 1024 + +## The TCP send timeout for the WebSocket/SSL connection. +## +## See: listener.tcp.$name.send_timeout +## +## Value: Duration +listener.wss.external.send_timeout = 15s + +## Close the WebSocket/SSL connection if send timeout. +## +## See: listener.tcp.$name.send_timeout_close +## +## Value: on | off +listener.wss.external.send_timeout_close = on + +## The TCP receive buffer(os kernel) for the WebSocket/SSL connections. +## +## See: listener.tcp.$name.recbuf +## +## Value: Bytes +## listener.wss.external.recbuf = 4KB + +## The TCP send buffer(os kernel) for the WebSocket/SSL connections. +## +## See: listener.tcp.$name.sndbuf +## +## Value: Bytes +## listener.wss.external.sndbuf = 4KB + +## The size of the user-level software buffer used by the driver. +## +## See: listener.tcp.$name.buffer +## +## Value: Bytes +## listener.wss.external.buffer = 4KB + +## The TCP_NODELAY flag for WebSocket/SSL connections. +## +## See: listener.tcp.$name.nodelay +## +## Value: true | false +## listener.wss.external.nodelay = true + +## The compress flag for external WebSocket/SSL connections. +## +## If this Value is set true,the websocket message would be compressed +## +## Value: true | false +## listener.wss.external.compress = true + +## The level of deflate options for external WebSocket/SSL connections. +## +## See: listener.wss.$name.deflate_opts.level +## +## Value: none | default | best_compression | best_speed +## listener.wss.external.deflate_opts.level = default + +## The mem_level of deflate options for external WebSocket/SSL connections. +## +## See: listener.wss.$name.deflate_opts.mem_level +## +## Valid range is 1-9 +## listener.wss.external.deflate_opts.mem_level = 8 + +## The strategy of deflate options for external WebSocket/SSL connections. +## +## See: listener.wss.$name.deflate_opts.strategy +## +## Value: default | filtered | huffman_only | rle +## listener.wss.external.deflate_opts.strategy = default + +## The deflate option for external WebSocket/SSL connections. +## +## See: listener.wss.$name.deflate_opts.server_context_takeover +## +## Value: takeover | no_takeover +## listener.wss.external.deflate_opts.server_context_takeover = takeover + +## The deflate option for external WebSocket/SSL connections. +## +## See: listener.wss.$name.deflate_opts.client_context_takeover +## +## Value: takeover | no_takeover +## listener.wss.external.deflate_opts.client_context_takeover = takeover + +## The deflate options for external WebSocket/SSL connections. +## +## See: listener.wss.$name.deflate_opts.server_max_window_bits +## +## Valid range is 8-15 +## listener.wss.external.deflate_opts.server_max_window_bits = 15 + +## The deflate options for external WebSocket/SSL connections. +## +## See: listener.wss.$name.deflate_opts.client_max_window_bits +## +## Valid range is 8-15 +## listener.wss.external.deflate_opts.client_max_window_bits = 15 + +## The idle timeout for external WebSocket/SSL connections. +## +## See: listener.wss.$name.idle_timeout +## +## Value: Duration +## listener.wss.external.idle_timeout = 60s + +## The max frame size for external WebSocket/SSL connections. +## +## Value: Number +## listener.wss.external.max_frame_size = 0 + +## Whether a WebSocket message is allowed to contain multiple MQTT packets +## +## Value: single | multiple +listener.wss.external.mqtt_piggyback = multiple + +##------------------------------------------------------------------- +## Plugins +##------------------------------------------------------------------- + +## The etc dir for plugins' config. +## +## Value: Folder +plugins.etc_dir = {{ platform_etc_dir }}/plugins/ + +## The file to store loaded plugin names. +## +## Value: File +plugins.loaded_file = {{ platform_data_dir }}/loaded_plugins + +## The directory of extension plugins. +## +## Value: File +plugins.expand_plugins_dir = {{ platform_plugins_dir }}/ + ##-------------------------------------------------------------------- ## Broker ##-------------------------------------------------------------------- @@ -268,7 +2031,7 @@ broker.sys_heartbeat = 30s ## ## Value: Enum ## - local -## - one +## - leader ## - quorum ## - all broker.session_locking_strategy = quorum @@ -279,7 +2042,9 @@ broker.session_locking_strategy = quorum ## - random ## - round_robin ## - sticky -## - hash +## - hash # same as hash_clientid +## - hash_clientid +## - hash_topic broker.shared_subscription_strategy = random ## Enable/disable shared dispatch acknowledgement for QoS1 and QoS2 messages @@ -296,18 +2061,153 @@ broker.shared_dispatch_ack_enabled = false ## Value: Flag broker.route_batch_clean = off -##------------------------------------------------------------------- -## Plugins -##------------------------------------------------------------------- +##-------------------------------------------------------------------- +## System Monitor +##-------------------------------------------------------------------- -## The etc dir for plugins' config. +## Enable Long GC monitoring. Disable if the value is 0. +## Notice: don't enable the monitor in production for: +## https://github.com/erlang/otp/blob/feb45017da36be78d4c5784d758ede619fa7bfd3/erts/emulator/beam/erl_gc.c#L421 ## -## Value: Folder -plugins.etc_dir = {{ platform_etc_dir }}/plugins/ +## Value: Duration +## - h: hour +## - m: minute +## - s: second +## - ms: milliseconds +## +## Examples: +## - 2h: 2 hours +## - 30m: 30 minutes +## - 0.1s: 0.1 seconds +## - 100ms : 100 milliseconds +## +## Default: 0ms +sysmon.long_gc = 0 -## The file to store loaded plugin names. +## Enable Long Schedule(ms) monitoring. ## -## Value: File -plugins.loaded_file = {{ platform_data_dir }}/loaded_plugins +## See: http://erlang.org/doc/man/erlang.html#system_monitor-2 +## +## Value: Duration +## - h: hour +## - m: minute +## - s: second +## - ms: milliseconds +## +## Examples: +## - 2h: 2 hours +## - 30m: 30 minutes +## - 0.1s: 0.1 seconds +## - 100ms: 100 milliseconds +## +## Default: 0ms +sysmon.long_schedule = 240ms + +## Enable Large Heap monitoring. +## +## See: http://erlang.org/doc/man/erlang.html#system_monitor-2 +## +## Value: bytes +## +## Default: 8M words. 32MB on 32-bit VM, 64MB on 64-bit VM. +sysmon.large_heap = 8MB + +## Enable Busy Port monitoring. +## +## See: http://erlang.org/doc/man/erlang.html#system_monitor-2 +## +## Value: true | false +sysmon.busy_port = false + +## Enable Busy Dist Port monitoring. +## +## See: http://erlang.org/doc/man/erlang.html#system_monitor-2 +## +## Value: true | false +sysmon.busy_dist_port = true + +## The time interval for the periodic cpu check +## +## Value: Duration +## -h: hour, e.g. '2h' for 2 hours +## -m: minute, e.g. '5m' for 5 minutes +## -s: second, e.g. '30s' for 30 seconds +## +## Default: 60s +os_mon.cpu_check_interval = 60s + +## The threshold, as percentage of system cpu, for how much system cpu can be used before the corresponding alarm is set. +## +## Default: 80% +os_mon.cpu_high_watermark = 80% + +## The threshold, as percentage of system cpu, for how much system cpu can be used before the corresponding alarm is clear. +## +## Default: 60% +os_mon.cpu_low_watermark = 60% + +## The time interval for the periodic memory check +## +## Value: Duration +## -h: hour, e.g. '2h' for 2 hours +## -m: minute, e.g. '5m' for 5 minutes +## -s: second, e.g. '30s' for 30 seconds +## +## Default: 60s +os_mon.mem_check_interval = 60s + +## The threshold, as percentage of system memory, for how much system memory can be allocated before the corresponding alarm is set. +## +## Default: 70% +os_mon.sysmem_high_watermark = 70% + +## The threshold, as percentage of system memory, for how much system memory can be allocated by one Erlang process before the corresponding alarm is set. +## +## Default: 5% +os_mon.procmem_high_watermark = 5% + +## The time interval for the periodic process limit check +## +## Value: Duration +## +## Default: 30s +vm_mon.check_interval = 30s + +## The threshold, as percentage of processes, for how many processes can simultaneously exist at the local node before the corresponding alarm is set. +## +## Default: 80% +vm_mon.process_high_watermark = 80% + +## The threshold, as percentage of processes, for how many processes can simultaneously exist at the local node before the corresponding alarm is clear. +## +## Default: 60% +vm_mon.process_low_watermark = 60% + +## Specifies the actions to take when an alarm is activated +## +## Value: String +## - log +## - publish +## +## Default: log,publish +alarm.actions = log,publish + +## The maximum number of deactivated alarms +## +## Value: Integer +## +## Default: 1000 +alarm.size_limit = 1000 + +## Validity Period of deactivated alarms +## +## Value: Duration +## - h: hour +## - m: minute +## - s: second +## - ms: milliseconds +## +## Default: 24h +alarm.validity_period = 24h {{ additional_configs }} diff --git a/etc/listeners.conf b/etc/listeners.conf deleted file mode 100644 index 034321b2a..000000000 --- a/etc/listeners.conf +++ /dev/null @@ -1,940 +0,0 @@ -##-------------------------------------------------------------------- -## Listeners -##-------------------------------------------------------------------- - -##-------------------------------------------------------------------- -## MQTT/TCP - External TCP Listener for MQTT Protocol - -## listener.tcp.$name is the IP address and port that the MQTT/TCP -## listener will bind. -## -## Value: IP:Port | Port -## -## Examples: 1883, 127.0.0.1:1883, ::1:1883 -listener.tcp.external = 0.0.0.0:1883 - -## The acceptor pool for external MQTT/TCP listener. -## -## Value: Number -listener.tcp.external.acceptors = 8 - -## Maximum number of concurrent MQTT/TCP connections. -## -## Value: Number -listener.tcp.external.max_connections = 1024000 - -## Maximum external connections per second. -## -## Value: Number -listener.tcp.external.max_conn_rate = 1000 - -## Specify the {active, N} option for the external MQTT/TCP Socket. -## -## Value: Number -listener.tcp.external.active_n = 100 - -## Zone of the external MQTT/TCP listener belonged to. -## -## See: zone.$name.* -## -## Value: String -listener.tcp.external.zone = external - -## The access control rules for the MQTT/TCP listener. -## -## See: https://github.com/emqtt/esockd#allowdeny -## -## Value: ACL Rule -## -## Example: allow 192.168.0.0/24 -listener.tcp.external.access.1 = allow all - -## Enable the Proxy Protocol V1/2 if the EMQ X cluster is deployed -## behind HAProxy or Nginx. -## -## See: https://www.haproxy.com/blog/haproxy/proxy-protocol/ -## -## Value: on | off -## listener.tcp.external.proxy_protocol = on - -## Sets the timeout for proxy protocol. EMQ X will close the TCP connection -## if no proxy protocol packet recevied within the timeout. -## -## Value: Duration -## listener.tcp.external.proxy_protocol_timeout = 3s - -## Enable the option for X.509 certificate based authentication. -## EMQX will use the common name of certificate as MQTT username. -## -## Value: cn | dn | crt -## listener.tcp.external.peer_cert_as_username = cn - -## The TCP backlog defines the maximum length that the queue of pending -## connections can grow to. -## -## Value: Number >= 0 -listener.tcp.external.backlog = 1024 - -## The TCP send timeout for external MQTT connections. -## -## Value: Duration -listener.tcp.external.send_timeout = 15s - -## Close the TCP connection if send timeout. -## -## Value: on | off -listener.tcp.external.send_timeout_close = on - -## The TCP receive buffer(os kernel) for MQTT connections. -## -## See: http://erlang.org/doc/man/inet.html -## -## Value: Bytes -## listener.tcp.external.recbuf = 2KB - -## The TCP send buffer(os kernel) for MQTT connections. -## -## See: http://erlang.org/doc/man/inet.html -## -## Value: Bytes -## listener.tcp.external.sndbuf = 2KB - -## The size of the user-level software buffer used by the driver. -## Not to be confused with options sndbuf and recbuf, which correspond -## to the Kernel socket buffers. It is recommended to have val(buffer) -## >= max(val(sndbuf),val(recbuf)) to avoid performance issues because -## of unnecessary copying. val(buffer) is automatically set to the above -## maximum when values sndbuf or recbuf are set. -## -## See: http://erlang.org/doc/man/inet.html -## -## Value: Bytes -## listener.tcp.external.buffer = 2KB - -## Sets the 'buffer = max(sndbuf, recbuf)' if this option is enabled. -## -## Value: on | off -## listener.tcp.external.tune_buffer = off - -## The socket is set to a busy state when the amount of data queued internally -## by the ERTS socket implementation reaches this limit. -## -## Value: on | off -## Defaults to 1MB -## listener.tcp.external.high_watermark = 1MB - -## The TCP_NODELAY flag for MQTT connections. Small amounts of data are -## sent immediately if the option is enabled. -## -## Value: true | false -listener.tcp.external.nodelay = true - -## The SO_REUSEADDR flag for TCP listener. -## -## Value: true | false -listener.tcp.external.reuseaddr = true - -##-------------------------------------------------------------------- -## Internal TCP Listener for MQTT Protocol - -## The IP address and port that the internal MQTT/TCP protocol listener -## will bind. -## -## Value: IP:Port, Port -## -## Examples: 11883, 127.0.0.1:11883, ::1:11883 -listener.tcp.internal = 127.0.0.1:11883 - -## The acceptor pool for internal MQTT/TCP listener. -## -## Value: Number -listener.tcp.internal.acceptors = 4 - -## Maximum number of concurrent MQTT/TCP connections. -## -## Value: Number -listener.tcp.internal.max_connections = 1024000 - -## Maximum internal connections per second. -## -## Value: Number -listener.tcp.internal.max_conn_rate = 1000 - -## Specify the {active, N} option for the internal MQTT/TCP Socket. -## -## Value: Number -listener.tcp.internal.active_n = 1000 - -## Zone of the internal MQTT/TCP listener belonged to. -## -## Value: String -listener.tcp.internal.zone = internal - -## The TCP backlog of internal MQTT/TCP Listener. -## -## See: listener.tcp.$name.backlog -## -## Value: Number >= 0 -listener.tcp.internal.backlog = 512 - -## The TCP send timeout for internal MQTT connections. -## -## See: listener.tcp.$name.send_timeout -## -## Value: Duration -listener.tcp.internal.send_timeout = 5s - -## Close the MQTT/TCP connection if send timeout. -## -## See: listener.tcp.$name.send_timeout_close -## -## Value: on | off -listener.tcp.internal.send_timeout_close = on - -## The TCP receive buffer(os kernel) for internal MQTT connections. -## -## See: listener.tcp.$name.recbuf -## -## Value: Bytes -listener.tcp.internal.recbuf = 64KB - -## The TCP send buffer(os kernel) for internal MQTT connections. -## -## See: http://erlang.org/doc/man/inet.html -## -## Value: Bytes -listener.tcp.internal.sndbuf = 64KB - -## The size of the user-level software buffer used by the driver. -## -## See: listener.tcp.$name.buffer -## -## Value: Bytes -## listener.tcp.internal.buffer = 16KB - -## Sets the 'buffer = max(sndbuf, recbuf)' if this option is enabled. -## -## See: listener.tcp.$name.tune_buffer -## -## Value: on | off -## listener.tcp.internal.tune_buffer = off - -## The TCP_NODELAY flag for internal MQTT connections. -## -## See: listener.tcp.$name.nodelay -## -## Value: true | false -listener.tcp.internal.nodelay = false - -## The SO_REUSEADDR flag for MQTT/TCP Listener. -## -## Value: true | false -listener.tcp.internal.reuseaddr = true - -##-------------------------------------------------------------------- -## MQTT/SSL - External SSL Listener for MQTT Protocol - -## listener.ssl.$name is the IP address and port that the MQTT/SSL -## listener will bind. -## -## Value: IP:Port | Port -## -## Examples: 8883, 127.0.0.1:8883, ::1:8883 -listener.ssl.external = 8883 - -## The acceptor pool for external MQTT/SSL listener. -## -## Value: Number -listener.ssl.external.acceptors = 16 - -## Maximum number of concurrent MQTT/SSL connections. -## -## Value: Number -listener.ssl.external.max_connections = 102400 - -## Maximum MQTT/SSL connections per second. -## -## Value: Number -listener.ssl.external.max_conn_rate = 500 - -## Specify the {active, N} option for the internal MQTT/SSL Socket. -## -## Value: Number -listener.ssl.external.active_n = 100 - -## Zone of the external MQTT/SSL listener belonged to. -## -## Value: String -listener.ssl.external.zone = external - -## The access control rules for the MQTT/SSL listener. -## -## See: listener.tcp.$name.access -## -## Value: ACL Rule -listener.ssl.external.access.1 = allow all - -## Enable the Proxy Protocol V1/2 if the EMQ cluster is deployed behind -## HAProxy or Nginx. -## -## See: listener.tcp.$name.proxy_protocol -## -## Value: on | off -## listener.ssl.external.proxy_protocol = on - -## Sets the timeout for proxy protocol. -## -## See: listener.tcp.$name.proxy_protocol_timeout -## -## Value: Duration -## listener.ssl.external.proxy_protocol_timeout = 3s - -## TLS versions only to protect from POODLE attack. -## -## See: http://erlang.org/doc/man/ssl.html -## -## Value: String, seperated by ',' -## listener.ssl.external.tls_versions = tlsv1.2,tlsv1.1,tlsv1 - -## TLS Handshake timeout. -## -## Value: Duration -listener.ssl.external.handshake_timeout = 15s - -## Maximum number of non-self-issued intermediate certificates that can follow the peer certificate in a valid certification path. -## -## Value: Number -## listener.ssl.external.depth = 10 - -## Path to the file containing the user's private PEM-encoded key. -## -## See: http://erlang.org/doc/man/ssl.html -## -## Value: File -listener.ssl.external.keyfile = {{ platform_etc_dir }}/certs/key.pem - -## Path to a file containing the user certificate. -## -## See: http://erlang.org/doc/man/ssl.html -## -## Value: File -listener.ssl.external.certfile = {{ platform_etc_dir }}/certs/cert.pem - -## Path to the file containing PEM-encoded CA certificates. The CA certificates -## are used during server authentication and when building the client certificate chain. -## -## Value: File -## listener.ssl.external.cacertfile = {{ platform_etc_dir }}/certs/cacert.pem - -## The Ephemeral Diffie-Helman key exchange is a very effective way of -## ensuring Forward Secrecy by exchanging a set of keys that never hit -## the wire. Since the DH key is effectively signed by the private key, -## it needs to be at least as strong as the private key. In addition, -## the default DH groups that most of the OpenSSL installations have -## are only a handful (since they are distributed with the OpenSSL -## package that has been built for the operating system it’s running on) -## and hence predictable (not to mention, 1024 bits only). -## In order to escape this situation, first we need to generate a fresh, -## strong DH group, store it in a file and then use the option above, -## to force our SSL application to use the new DH group. Fortunately, -## OpenSSL provides us with a tool to do that. Simply run: -## openssl dhparam -out dh-params.pem 2048 -## -## Value: File -## listener.ssl.external.dhfile = {{ platform_etc_dir }}/certs/dh-params.pem - -## A server only does x509-path validation in mode verify_peer, -## as it then sends a certificate request to the client (this -## message is not sent if the verify option is verify_none). -## You can then also want to specify option fail_if_no_peer_cert. -## More information at: http://erlang.org/doc/man/ssl.html -## -## Value: verify_peer | verify_none -## listener.ssl.external.verify = verify_peer - -## Used together with {verify, verify_peer} by an SSL server. If set to true, -## the server fails if the client does not have a certificate to send, that is, -## sends an empty certificate. -## -## Value: true | false -## listener.ssl.external.fail_if_no_peer_cert = true - -## This is the single most important configuration option of an Erlang SSL -## application. Ciphers (and their ordering) define the way the client and -## server encrypt information over the wire, from the initial Diffie-Helman -## key exchange, the session key encryption ## algorithm and the message -## digest algorithm. Selecting a good cipher suite is critical for the -## application’s data security, confidentiality and performance. -## -## The cipher list above offers: -## -## A good balance between compatibility with older browsers. -## It can get stricter for Machine-To-Machine scenarios. -## Perfect Forward Secrecy. -## No old/insecure encryption and HMAC algorithms -## -## Most of it was copied from Mozilla’s Server Side TLS article -## -## Value: Ciphers -listener.ssl.external.ciphers = ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES256-SHA384,ECDHE-RSA-AES256-SHA384,ECDHE-ECDSA-DES-CBC3-SHA,ECDH-ECDSA-AES256-GCM-SHA384,ECDH-RSA-AES256-GCM-SHA384,ECDH-ECDSA-AES256-SHA384,ECDH-RSA-AES256-SHA384,DHE-DSS-AES256-GCM-SHA384,DHE-DSS-AES256-SHA256,AES256-GCM-SHA384,AES256-SHA256,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES128-SHA256,ECDHE-RSA-AES128-SHA256,ECDH-ECDSA-AES128-GCM-SHA256,ECDH-RSA-AES128-GCM-SHA256,ECDH-ECDSA-AES128-SHA256,ECDH-RSA-AES128-SHA256,DHE-DSS-AES128-GCM-SHA256,DHE-DSS-AES128-SHA256,AES128-GCM-SHA256,AES128-SHA256,ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,DHE-DSS-AES256-SHA,ECDH-ECDSA-AES256-SHA,ECDH-RSA-AES256-SHA,AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,DHE-DSS-AES128-SHA,ECDH-ECDSA-AES128-SHA,ECDH-RSA-AES128-SHA,AES128-SHA - -## Ciphers for TLS PSK. -## Note that 'listener.ssl.external.ciphers' and 'listener.ssl.external.psk_ciphers' cannot -## be configured at the same time. -## See 'https://tools.ietf.org/html/rfc4279#section-2'. -#listener.ssl.external.psk_ciphers = PSK-AES128-CBC-SHA,PSK-AES256-CBC-SHA,PSK-3DES-EDE-CBC-SHA,PSK-RC4-SHA - -## SSL parameter renegotiation is a feature that allows a client and a server -## to renegotiate the parameters of the SSL connection on the fly. -## RFC 5746 defines a more secure way of doing this. By enabling secure renegotiation, -## you drop support for the insecure renegotiation, prone to MitM attacks. -## -## Value: on | off -## listener.ssl.external.secure_renegotiate = off - -## A performance optimization setting, it allows clients to reuse -## pre-existing sessions, instead of initializing new ones. -## Read more about it here. -## -## See: http://erlang.org/doc/man/ssl.html -## -## Value: on | off -## listener.ssl.external.reuse_sessions = on - -## An important security setting, it forces the cipher to be set based -## on the server-specified order instead of the client-specified order, -## hence enforcing the (usually more properly configured) security -## ordering of the server administrator. -## -## Value: on | off -## listener.ssl.external.honor_cipher_order = on - -## Use the CN, DN or CRT field from the client certificate as a username. -## Notice that 'verify' should be set as 'verify_peer'. -## -## Value: cn | dn | crt -## listener.ssl.external.peer_cert_as_username = cn - -## TCP backlog for the SSL connection. -## -## See listener.tcp.$name.backlog -## -## Value: Number >= 0 -## listener.ssl.external.backlog = 1024 - -## The TCP send timeout for the SSL connection. -## -## See listener.tcp.$name.send_timeout -## -## Value: Duration -## listener.ssl.external.send_timeout = 15s - -## Close the SSL connection if send timeout. -## -## See: listener.tcp.$name.send_timeout_close -## -## Value: on | off -## listener.ssl.external.send_timeout_close = on - -## The TCP receive buffer(os kernel) for the SSL connections. -## -## See: listener.tcp.$name.recbuf -## -## Value: Bytes -## listener.ssl.external.recbuf = 4KB - -## The TCP send buffer(os kernel) for internal MQTT connections. -## -## See: listener.tcp.$name.sndbuf -## -## Value: Bytes -## listener.ssl.external.sndbuf = 4KB - -## The size of the user-level software buffer used by the driver. -## -## See: listener.tcp.$name.buffer -## -## Value: Bytes -## listener.ssl.external.buffer = 4KB - -## Sets the 'buffer = max(sndbuf, recbuf)' if this option is enabled. -## -## See: listener.tcp.$name.tune_buffer -## -## Value: on | off -## listener.ssl.external.tune_buffer = off - -## The TCP_NODELAY flag for SSL connections. -## -## See: listener.tcp.$name.nodelay -## -## Value: true | false -## listener.ssl.external.nodelay = true - -## The SO_REUSEADDR flag for MQTT/SSL Listener. -## -## Value: true | false -listener.ssl.external.reuseaddr = true - -##-------------------------------------------------------------------- -## External WebSocket listener for MQTT protocol - -## listener.ws.$name is the IP address and port that the MQTT/WebSocket -## listener will bind. -## -## Value: IP:Port | Port -## -## Examples: 8083, 127.0.0.1:8083, ::1:8083 -listener.ws.external = 8083 - -## The path of WebSocket MQTT endpoint -## -## Value: URL Path -listener.ws.external.mqtt_path = /mqtt - -## The acceptor pool for external MQTT/WebSocket listener. -## -## Value: Number -listener.ws.external.acceptors = 4 - -## Maximum number of concurrent MQTT/WebSocket connections. -## -## Value: Number -listener.ws.external.max_connections = 102400 - -## Maximum MQTT/WebSocket connections per second. -## -## Value: Number -listener.ws.external.max_conn_rate = 1000 - -## Simulate the {active, N} option for the MQTT/WebSocket connections. -## -## Value: Number -listener.ws.external.active_n = 100 - -## Zone of the external MQTT/WebSocket listener belonged to. -## -## Value: String -listener.ws.external.zone = external - -## The access control for the MQTT/WebSocket listener. -## -## See: listener.ws.$name.access -## -## Value: ACL Rule -listener.ws.external.access.1 = allow all - -## Verify if the protocol header is valid. Turn off for WeChat MiniApp. -## -## Value: on | off -listener.ws.external.verify_protocol_header = on - -## Enable the Proxy Protocol V1/2 if the EMQ cluster is deployed behind -## HAProxy or Nginx. -## -## See: listener.ws.$name.proxy_protocol -## -## Value: on | off -## listener.ws.external.proxy_protocol = on - -## Sets the timeout for proxy protocol. -## -## See: listener.ws.$name.proxy_protocol_timeout -## -## Value: Duration -## listener.ws.external.proxy_protocol_timeout = 3s - -## The TCP backlog of external MQTT/WebSocket Listener. -## -## See: listener.ws.$name.backlog -## -## Value: Number >= 0 -listener.ws.external.backlog = 1024 - -## The TCP send timeout for external MQTT/WebSocket connections. -## -## See: listener.ws.$name.send_timeout -## -## Value: Duration -listener.ws.external.send_timeout = 15s - -## Close the MQTT/WebSocket connection if send timeout. -## -## See: listener.ws.$name.send_timeout_close -## -## Value: on | off -listener.ws.external.send_timeout_close = on - -## The TCP receive buffer(os kernel) for external MQTT/WebSocket connections. -## -## See: listener.ws.$name.recbuf -## -## Value: Bytes -## listener.ws.external.recbuf = 2KB - -## The TCP send buffer(os kernel) for external MQTT/WebSocket connections. -## -## See: listener.ws.$name.sndbuf -## -## Value: Bytes -## listener.ws.external.sndbuf = 2KB - -## The size of the user-level software buffer used by the driver. -## -## See: listener.ws.$name.buffer -## -## Value: Bytes -## listener.ws.external.buffer = 2KB - -## Sets the 'buffer = max(sndbuf, recbuf)' if this option is enabled. -## -## See: listener.ws.$name.tune_buffer -## -## Value: on | off -## listener.ws.external.tune_buffer = off - -## The TCP_NODELAY flag for external MQTT/WebSocket connections. -## -## See: listener.ws.$name.nodelay -## -## Value: true | false -listener.ws.external.nodelay = true - -## The compress flag for external MQTT/WebSocket connections. -## -## If this Value is set true,the websocket message would be compressed -## -## Value: true | false -## listener.ws.external.compress = true - -## The level of deflate options for external MQTT/WebSocket connections. -## -## See: listener.ws.$name.deflate_opts.level -## -## Value: none | default | best_compression | best_speed -## listener.ws.external.deflate_opts.level = default - -## The mem_level of deflate options for external MQTT/WebSocket connections. -## -## See: listener.ws.$name.deflate_opts.mem_level -## -## Valid range is 1-9 -## listener.ws.external.deflate_opts.mem_level = 8 - -## The strategy of deflate options for external MQTT/WebSocket connections. -## -## See: listener.ws.$name.deflate_opts.strategy -## -## Value: default | filtered | huffman_only | rle -## listener.ws.external.deflate_opts.strategy = default - -## The deflate option for external MQTT/WebSocket connections. -## -## See: listener.ws.$name.deflate_opts.server_context_takeover -## -## Value: takeover | no_takeover -## listener.ws.external.deflate_opts.server_context_takeover = takeover - -## The deflate option for external MQTT/WebSocket connections. -## -## See: listener.ws.$name.deflate_opts.client_context_takeover -## -## Value: takeover | no_takeover -## listener.ws.external.deflate_opts.client_context_takeover = takeover - -## The deflate options for external MQTT/WebSocket connections. -## -## See: listener.ws.$name.deflate_opts.server_max_window_bits -## -## Valid range is 8-15 -## listener.ws.external.deflate_opts.server_max_window_bits = 15 - -## The deflate options for external MQTT/WebSocket connections. -## -## See: listener.ws.$name.deflate_opts.client_max_window_bits -## -## Valid range is 8-15 -## listener.ws.external.deflate_opts.client_max_window_bits = 15 - -## The idle timeout for external MQTT/WebSocket connections. -## -## See: listener.ws.$name.idle_timeout -## -## Value: Duration -## listener.ws.external.idle_timeout = 60s - -## The max frame size for external MQTT/WebSocket connections. -## -## -## Value: Number -## listener.ws.external.max_frame_size = 0 - -## Whether a WebSocket message is allowed to contain multiple MQTT packets -## -## Value: single | multiple -listener.ws.external.mqtt_piggyback = multiple - -##-------------------------------------------------------------------- -## External WebSocket/SSL listener for MQTT Protocol - -## listener.wss.$name is the IP address and port that the MQTT/WebSocket/SSL -## listener will bind. -## -## Value: IP:Port | Port -## -## Examples: 8084, 127.0.0.1:8084, ::1:8084 -listener.wss.external = 8084 - -## The path of WebSocket MQTT endpoint -## -## Value: URL Path -listener.wss.external.mqtt_path = /mqtt - -## The acceptor pool for external MQTT/WebSocket/SSL listener. -## -## Value: Number -listener.wss.external.acceptors = 4 - -## Maximum number of concurrent MQTT/Webwocket/SSL connections. -## -## Value: Number -listener.wss.external.max_connections = 16 - -## Maximum MQTT/WebSocket/SSL connections per second. -## -## See: listener.tcp.$name.max_conn_rate -## -## Value: Number -listener.wss.external.max_conn_rate = 1000 - -## Simulate the {active, N} option for the MQTT/WebSocket/SSL connections. -## -## Value: Number -listener.wss.external.active_n = 100 - -## Zone of the external MQTT/WebSocket/SSL listener belonged to. -## -## Value: String -listener.wss.external.zone = external - -## The access control rules for the MQTT/WebSocket/SSL listener. -## -## See: listener.tcp.$name.access. -## -## Value: ACL Rule -listener.wss.external.access.1 = allow all - -## See: listener.ws.external.verify_protocol_header -## -## Value: on | off -listener.wss.external.verify_protocol_header = on - -## Enable the Proxy Protocol V1/2 support. -## -## See: listener.tcp.$name.proxy_protocol -## -## Value: on | off -## listener.wss.external.proxy_protocol = on - -## Sets the timeout for proxy protocol. -## -## See: listener.tcp.$name.proxy_protocol_timeout -## -## Value: Duration -## listener.wss.external.proxy_protocol_timeout = 3s - -## TLS versions only to protect from POODLE attack. -## -## See: listener.ssl.$name.tls_versions -## -## Value: String, seperated by ',' -## listener.wss.external.tls_versions = tlsv1.2,tlsv1.1,tlsv1 - -## Path to the file containing the user's private PEM-encoded key. -## -## See: listener.ssl.$name.keyfile -## -## Value: File -listener.wss.external.keyfile = {{ platform_etc_dir }}/certs/key.pem - -## Path to a file containing the user certificate. -## -## See: listener.ssl.$name.certfile -## -## Value: File -listener.wss.external.certfile = {{ platform_etc_dir }}/certs/cert.pem - -## Path to the file containing PEM-encoded CA certificates. -## -## See: listener.ssl.$name.cacert -## -## Value: File -## listener.wss.external.cacertfile = {{ platform_etc_dir }}/certs/cacert.pem - -## See: listener.ssl.$name.dhfile -## -## Value: File -## listener.ssl.external.dhfile = {{ platform_etc_dir }}/certs/dh-params.pem - -## See: listener.ssl.$name.vefify -## -## Value: vefify_peer | verify_none -## listener.wss.external.verify = verify_peer - -## See: listener.ssl.$name.fail_if_no_peer_cert -## -## Value: false | true -## listener.wss.external.fail_if_no_peer_cert = true - -## See: listener.ssl.$name.ciphers -## -## Value: Ciphers -listener.wss.external.ciphers = ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES256-SHA384,ECDHE-RSA-AES256-SHA384,ECDHE-ECDSA-DES-CBC3-SHA,ECDH-ECDSA-AES256-GCM-SHA384,ECDH-RSA-AES256-GCM-SHA384,ECDH-ECDSA-AES256-SHA384,ECDH-RSA-AES256-SHA384,DHE-DSS-AES256-GCM-SHA384,DHE-DSS-AES256-SHA256,AES256-GCM-SHA384,AES256-SHA256,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES128-SHA256,ECDHE-RSA-AES128-SHA256,ECDH-ECDSA-AES128-GCM-SHA256,ECDH-RSA-AES128-GCM-SHA256,ECDH-ECDSA-AES128-SHA256,ECDH-RSA-AES128-SHA256,DHE-DSS-AES128-GCM-SHA256,DHE-DSS-AES128-SHA256,AES128-GCM-SHA256,AES128-SHA256,ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,DHE-DSS-AES256-SHA,ECDH-ECDSA-AES256-SHA,ECDH-RSA-AES256-SHA,AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,DHE-DSS-AES128-SHA,ECDH-ECDSA-AES128-SHA,ECDH-RSA-AES128-SHA,AES128-SHA - -## Ciphers for TLS PSK. -## Note that 'listener.wss.external.ciphers' and 'listener.wss.external.psk_ciphers' cannot -## be configured at the same time. -## See 'https://tools.ietf.org/html/rfc4279#section-2'. -## listener.wss.external.psk_ciphers = PSK-AES128-CBC-SHA,PSK-AES256-CBC-SHA,PSK-3DES-EDE-CBC-SHA,PSK-RC4-SHA - -## See: listener.ssl.$name.secure_renegotiate -## -## Value: on | off -## listener.wss.external.secure_renegotiate = off - -## See: listener.ssl.$name.reuse_sessions -## -## Value: on | off -## listener.wss.external.reuse_sessions = on - -## See: listener.ssl.$name.honor_cipher_order -## -## Value: on | off -## listener.wss.external.honor_cipher_order = on - -## See: listener.ssl.$name.peer_cert_as_username -## -## Value: cn | dn | crt -## listener.wss.external.peer_cert_as_username = cn - -## TCP backlog for the WebSocket/SSL connection. -## -## See: listener.tcp.$name.backlog -## -## Value: Number >= 0 -listener.wss.external.backlog = 1024 - -## The TCP send timeout for the WebSocket/SSL connection. -## -## See: listener.tcp.$name.send_timeout -## -## Value: Duration -listener.wss.external.send_timeout = 15s - -## Close the WebSocket/SSL connection if send timeout. -## -## See: listener.tcp.$name.send_timeout_close -## -## Value: on | off -listener.wss.external.send_timeout_close = on - -## The TCP receive buffer(os kernel) for the WebSocket/SSL connections. -## -## See: listener.tcp.$name.recbuf -## -## Value: Bytes -## listener.wss.external.recbuf = 4KB - -## The TCP send buffer(os kernel) for the WebSocket/SSL connections. -## -## See: listener.tcp.$name.sndbuf -## -## Value: Bytes -## listener.wss.external.sndbuf = 4KB - -## The size of the user-level software buffer used by the driver. -## -## See: listener.tcp.$name.buffer -## -## Value: Bytes -## listener.wss.external.buffer = 4KB - -## The TCP_NODELAY flag for WebSocket/SSL connections. -## -## See: listener.tcp.$name.nodelay -## -## Value: true | false -## listener.wss.external.nodelay = true - -## The compress flag for external WebSocket/SSL connections. -## -## If this Value is set true,the websocket message would be compressed -## -## Value: true | false -## listener.wss.external.compress = true - -## The level of deflate options for external WebSocket/SSL connections. -## -## See: listener.wss.$name.deflate_opts.level -## -## Value: none | default | best_compression | best_speed -## listener.wss.external.deflate_opts.level = default - -## The mem_level of deflate options for external WebSocket/SSL connections. -## -## See: listener.wss.$name.deflate_opts.mem_level -## -## Valid range is 1-9 -## listener.wss.external.deflate_opts.mem_level = 8 - -## The strategy of deflate options for external WebSocket/SSL connections. -## -## See: listener.wss.$name.deflate_opts.strategy -## -## Value: default | filtered | huffman_only | rle -## listener.wss.external.deflate_opts.strategy = default - -## The deflate option for external WebSocket/SSL connections. -## -## See: listener.wss.$name.deflate_opts.server_context_takeover -## -## Value: takeover | no_takeover -## listener.wss.external.deflate_opts.server_context_takeover = takeover - -## The deflate option for external WebSocket/SSL connections. -## -## See: listener.wss.$name.deflate_opts.client_context_takeover -## -## Value: takeover | no_takeover -## listener.wss.external.deflate_opts.client_context_takeover = takeover - -## The deflate options for external WebSocket/SSL connections. -## -## See: listener.wss.$name.deflate_opts.server_max_window_bits -## -## Valid range is 8-15 -## listener.wss.external.deflate_opts.server_max_window_bits = 15 - -## The deflate options for external WebSocket/SSL connections. -## -## See: listener.wss.$name.deflate_opts.client_max_window_bits -## -## Valid range is 8-15 -## listener.wss.external.deflate_opts.client_max_window_bits = 15 - -## The idle timeout for external WebSocket/SSL connections. -## -## See: listener.wss.$name.idle_timeout -## -## Value: Duration -## listener.wss.external.idle_timeout = 60s - -## The max frame size for external WebSocket/SSL connections. -## -## Value: Number -## listener.wss.external.max_frame_size = 0 - -## Whether a WebSocket message is allowed to contain multiple MQTT packets -## -## Value: single | multiple -listener.wss.external.mqtt_piggyback = multiple diff --git a/etc/logger.conf b/etc/logger.conf deleted file mode 100644 index 4fd0faf5f..000000000 --- a/etc/logger.conf +++ /dev/null @@ -1,170 +0,0 @@ -##-------------------------------------------------------------------- -## Log -##-------------------------------------------------------------------- - -## Where to emit the logs. -## Enable the console (standard output) logs. -## -## Value: off | file | console | both -## - off: disable logs entirely -## - file: write logs only to file -## - console: write logs only to standard I/O -## - both: write logs both to file and standard I/O -log.to = both - -## The log severity level. -## -## Value: debug | info | notice | warning | error | critical | alert | emergency -## -## Note: Only the messages with severity level higher than or equal to -## this level will be logged. -## -## Default: warning -log.level = warning - -## The dir for log files. -## -## Value: Folder -log.dir = {{ platform_log_dir }} - -## The log filename for logs of level specified in "log.level". -## -## If `log.rotation` is enabled, this is the base name of the -## files. Each file in a rotated log is named .N, where N is an integer. -## -## Value: String -## Default: emqx.log -log.file = emqx.log - -## Limits the total number of characters printed for each log event. -## -## Value: Integer -## Default: No Limit -#log.chars_limit = 8192 - -## Enables the log rotation. -## With this enabled, new log files will be created when the current -## log file is full, max to `log.rotation.size` files will be created. -## -## Value: on | off -## Default: on -log.rotation = on - -## Maximum size of each log file. -## -## Value: Number -## Default: 10M -## Supported Unit: KB | MB | GB -log.rotation.size = 10MB - -## Maximum rotation count of log files. -## -## Value: Number -## Default: 5 -log.rotation.count = 5 - -## To create additional log files for specific log levels. -## -## Value: File Name -## Format: log.$level.file = $filename, -## where "$level" can be one of: debug, info, notice, warning, -## error, critical, alert, emergency -## Note: Log files for a specific log level will only contain all the logs -## that higher than or equal to that level -## -#log.info.file = info.log -#log.error.file = error.log - -## The max allowed queue length before switching to sync mode. -## -## Log overload protection parameter. If the message queue grows -## larger than this value the handler switches from anync to sync mode. -## -## Default: 100 -## -#log.sync_mode_qlen = 100 - -## The max allowed queue length before switching to drop mode. -## -## Log overload protection parameter. When the message queue grows -## larger than this threshold, the handler switches to a mode in which -## it drops all new events that senders want to log. -## -## Default: 3000 -## -#log.drop_mode_qlen = 3000 - -## The max allowed queue length before switching to flush mode. -## -## Log overload protection parameter. If the length of the message queue -## grows larger than this threshold, a flush (delete) operation takes place. -## To flush events, the handler discards the messages in the message queue -## by receiving them in a loop without logging. -## -## Default: 8000 -## -#log.flush_qlen = 8000 - -## Kill the log handler when it gets overloaded. -## -## Log overload protection parameter. It is possible that a handler, -## even if it can successfully manage peaks of high load without crashing, -## can build up a large message queue, or use a large amount of memory. -## We could kill the log handler in these cases and restart it after a -## few seconds. -## -## Default: on -## -#log.overload_kill = on - -## The max allowed queue length before killing the log hanlder. -## -## Log overload protection parameter. This is the maximum allowed queue -## length. If the message queue grows larger than this, the handler -## process is terminated. -## -## Default: 20000 -## -#log.overload_kill_qlen = 20000 - -## The max allowed memory size before killing the log hanlder. -## -## Log overload protection parameter. This is the maximum memory size -## that the handler process is allowed to use. If the handler grows -## larger than this, the process is terminated. -## -## Default: 30MB -## -#log.overload_kill_mem_size = 30MB - -## Restart the log hanlder after some seconds. -## -## Log overload protection parameter. If the handler is terminated, -## it restarts automatically after a delay specified in seconds. -## The value "infinity" prevents restarts. -## -## Default: 5s -## -#log.overload_kill_restart_after = 5s - -## Max burst count and time window for burst control. -## -## Log overload protection parameter. Large bursts of log events - many -## events received by the handler under a short period of time - can -## potentially cause problems. By specifying the maximum number of events -## to be handled within a certain time frame, the handler can avoid -## choking the log with massive amounts of printouts. -## -## This config controls the maximum number of events to handle within -## a time frame. After the limit is reached, successive events are -## dropped until the end of the time frame. -## -## Note that there would be no warning if any messages were -## dropped because of burst control. -## -## Comment this config out to disable the burst control feature. -## -## Value: MaxBurstCount,TimeWindow -## Default: disabled -## -#log.burst_limit = 20000, 1s \ No newline at end of file diff --git a/etc/rpc.conf b/etc/rpc.conf deleted file mode 100644 index d86838e4f..000000000 --- a/etc/rpc.conf +++ /dev/null @@ -1,98 +0,0 @@ -##-------------------------------------------------------------------- -## RPC -##-------------------------------------------------------------------- -## RPC Mode. -## -## Value: sync | async -rpc.mode = async - -## Max batch size of async RPC requests. -## -## Value: Integer -## Zero or negative value disables rpc batching. -## -## NOTE: RPC batch won't work when rpc.mode = sync -rpc.async_batch_size = 256 - -## RPC port discovery -## -## The strategy for discovering the RPC listening port of other nodes. -## -## Value: Enum -## - manual: discover ports by `tcp_server_port` and `tcp_client_port`. -## - stateless: discover ports in a stateless manner. -## If node name is `emqx@127.0.0.1`, where the `` is an integer, -## then the listening port will be `5370 + ` -## -## Defaults to `stateless`. -rpc.port_discovery = stateless - -## TCP server port for RPC. -## -## Only takes effect when `rpc.port_discovery` = `manual`. -## -## Value: Port [1024-65535] -#rpc.tcp_server_port = 5369 - -## TCP port for outgoing RPC connections. -## -## Only takes effect when `rpc.port_discovery` = `manual`. -## -## Value: Port [1024-65535] -#rpc.tcp_client_port = 5369 - -## Number of outgoing RPC connections. -## -## Value: Interger [1-256] -## Defaults to NumberOfCPUSchedulers / 2 -#rpc.tcp_client_num = 1 - -## RCP Client connect timeout. -## -## Value: Seconds -rpc.connect_timeout = 5s - -## TCP send timeout of RPC client and server. -## -## Value: Seconds -rpc.send_timeout = 5s - -## Authentication timeout -## -## Value: Seconds -rpc.authentication_timeout = 5s - -## Default receive timeout for call() functions -## -## Value: Seconds -rpc.call_receive_timeout = 15s - -## Socket idle keepalive. -## -## Value: Seconds -rpc.socket_keepalive_idle = 900s - -## TCP Keepalive probes interval. -## -## Value: Seconds -rpc.socket_keepalive_interval = 75s - -## Probes lost to close the connection -## -## Value: Integer -rpc.socket_keepalive_count = 9 - -## Size of TCP send buffer. -## -## Value: Bytes -rpc.socket_sndbuf = 1MB - -## Size of TCP receive buffer. -## -## Value: Seconds -rpc.socket_recbuf = 1MB - -## Size of user-level software socket buffer. -## -## Value: Seconds -rpc.socket_buffer = 1MB diff --git a/etc/sys_mon.conf b/etc/sys_mon.conf deleted file mode 100644 index abd8baa04..000000000 --- a/etc/sys_mon.conf +++ /dev/null @@ -1,148 +0,0 @@ -##-------------------------------------------------------------------- -## System Monitor -##-------------------------------------------------------------------- - -## Enable Long GC monitoring. Disable if the value is 0. -## Notice: don't enable the monitor in production for: -## https://github.com/erlang/otp/blob/feb45017da36be78d4c5784d758ede619fa7bfd3/erts/emulator/beam/erl_gc.c#L421 -## -## Value: Duration -## - h: hour -## - m: minute -## - s: second -## - ms: milliseconds -## -## Examples: -## - 2h: 2 hours -## - 30m: 30 minutes -## - 0.1s: 0.1 seconds -## - 100ms : 100 milliseconds -## -## Default: 0ms -sysmon.long_gc = 0 - -## Enable Long Schedule(ms) monitoring. -## -## See: http://erlang.org/doc/man/erlang.html#system_monitor-2 -## -## Value: Duration -## - h: hour -## - m: minute -## - s: second -## - ms: milliseconds -## -## Examples: -## - 2h: 2 hours -## - 30m: 30 minutes -## - 0.1s: 0.1 seconds -## - 100ms: 100 milliseconds -## -## Default: 0ms -sysmon.long_schedule = 240ms - -## Enable Large Heap monitoring. -## -## See: http://erlang.org/doc/man/erlang.html#system_monitor-2 -## -## Value: bytes -## -## Default: 8M words. 32MB on 32-bit VM, 64MB on 64-bit VM. -sysmon.large_heap = 8MB - -## Enable Busy Port monitoring. -## -## See: http://erlang.org/doc/man/erlang.html#system_monitor-2 -## -## Value: true | false -sysmon.busy_port = false - -## Enable Busy Dist Port monitoring. -## -## See: http://erlang.org/doc/man/erlang.html#system_monitor-2 -## -## Value: true | false -sysmon.busy_dist_port = true - -## The time interval for the periodic cpu check -## -## Value: Duration -## -h: hour, e.g. '2h' for 2 hours -## -m: minute, e.g. '5m' for 5 minutes -## -s: second, e.g. '30s' for 30 seconds -## -## Default: 60s -os_mon.cpu_check_interval = 60s - -## The threshold, as percentage of system cpu, for how much system cpu can be used before the corresponding alarm is set. -## -## Default: 80% -os_mon.cpu_high_watermark = 80% - -## The threshold, as percentage of system cpu, for how much system cpu can be used before the corresponding alarm is clear. -## -## Default: 60% -os_mon.cpu_low_watermark = 60% - -## The time interval for the periodic memory check -## -## Value: Duration -## -h: hour, e.g. '2h' for 2 hours -## -m: minute, e.g. '5m' for 5 minutes -## -s: second, e.g. '30s' for 30 seconds -## -## Default: 60s -os_mon.mem_check_interval = 60s - -## The threshold, as percentage of system memory, for how much system memory can be allocated before the corresponding alarm is set. -## -## Default: 70% -os_mon.sysmem_high_watermark = 70% - -## The threshold, as percentage of system memory, for how much system memory can be allocated by one Erlang process before the corresponding alarm is set. -## -## Default: 5% -os_mon.procmem_high_watermark = 5% - -## The time interval for the periodic process limit check -## -## Value: Duration -## -## Default: 30s -vm_mon.check_interval = 30s - -## The threshold, as percentage of processes, for how many processes can simultaneously exist at the local node before the corresponding alarm is set. -## -## Default: 80% -vm_mon.process_high_watermark = 80% - -## The threshold, as percentage of processes, for how many processes can simultaneously exist at the local node before the corresponding alarm is clear. -## -## Default: 60% -vm_mon.process_low_watermark = 60% - -## Specifies the actions to take when an alarm is activated -## -## Value: String -## - log -## - publish -## -## Default: log,publish -alarm.actions = log,publish - -## The maximum number of deactivated alarms -## -## Value: Integer -## -## Default: 1000 -alarm.size_limit = 1000 - -## Validity Period of deactivated alarms -## -## Value: Duration -## - h: hour -## - m: minute -## - s: second -## - ms: milliseconds -## -## Default: 24h -alarm.validity_period = 24h \ No newline at end of file diff --git a/etc/zones.conf b/etc/zones.conf deleted file mode 100644 index ea1e1807c..000000000 --- a/etc/zones.conf +++ /dev/null @@ -1,327 +0,0 @@ -##-------------------------------------------------------------------- -## Zones -##-------------------------------------------------------------------- - -##-------------------------------------------------------------------- -## External Zone - -## Idle timeout of the external MQTT connections. -## -## Value: duration -zone.external.idle_timeout = 15s - -## Enable ACL check. -## -## Value: Flag -zone.external.enable_acl = on - -## Enable ban check. -## -## Value: Flag -zone.external.enable_ban = on - -## Enable per connection statistics. -## -## Value: on | off -zone.external.enable_stats = on - -## The action when acl check reject current operation -## -## Value: ignore | disconnect -## Default: ignore -zone.external.acl_deny_action = ignore - -## Force the MQTT connection process GC after this number of -## messages | bytes passed through. -## -## Numbers delimited by `|'. Zero or negative is to disable. -zone.external.force_gc_policy = 16000|16MB - -## Max message queue length and total heap size to force shutdown -## connection/session process. -## Message queue here is the Erlang process mailbox, but not the number -## of queued MQTT messages of QoS 1 and 2. -## -## Numbers delimited by `|'. Zero or negative is to disable. -zone.external.force_shutdown_policy = 10000|32MB - -## Maximum MQTT packet size allowed. -## -## Value: Bytes -## Default: 1MB -## zone.external.max_packet_size = 64KB - -## Maximum length of MQTT clientId allowed. -## -## Value: Number [23-65535] -## zone.external.max_clientid_len = 1024 - -## Maximum topic levels allowed. 0 means no limit. -## -## Value: Number -## zone.external.max_topic_levels = 7 - -## Maximum QoS allowed. -## -## Value: 0 | 1 | 2 -## zone.external.max_qos_allowed = 2 - -## Maximum Topic Alias, 0 means no limit. -## -## Value: 0-65535 -## zone.external.max_topic_alias = 65535 - -## Whether the Server supports retained messages. -## -## Value: boolean -## zone.external.retain_available = true - -## Whether the Server supports Wildcard Subscriptions -## -## Value: boolean -## zone.external.wildcard_subscription = false - -## Whether the Server supports Shared Subscriptions -## -## Value: boolean -## zone.external.shared_subscription = false - -## Server Keep Alive -## -## Value: Number -## zone.external.server_keepalive = 0 - -## The backoff for MQTT keepalive timeout. The broker will kick a connection out -## until 'Keepalive * backoff * 2' timeout. -## -## Value: Float > 0.5 -zone.external.keepalive_backoff = 0.75 - -## Maximum number of subscriptions allowed, 0 means no limit. -## -## Value: Number -zone.external.max_subscriptions = 0 - -## Force to upgrade QoS according to subscription. -## -## Value: on | off -zone.external.upgrade_qos = off - -## Maximum size of the Inflight Window storing QoS1/2 messages delivered but unacked. -## -## Value: Number -zone.external.max_inflight = 32 - -## Retry interval for QoS1/2 message delivering. -## -## Value: Duration -zone.external.retry_interval = 30s - -## Maximum QoS2 packets (Client -> Broker) awaiting PUBREL, 0 means no limit. -## -## Value: Number -zone.external.max_awaiting_rel = 100 - -## The QoS2 messages (Client -> Broker) will be dropped if awaiting PUBREL timeout. -## -## Value: Duration -zone.external.await_rel_timeout = 300s - -## Default session expiry interval for MQTT V3.1.1 connections. -## -## Value: Duration -## -d: day -## -h: hour -## -m: minute -## -s: second -## -## Default: 2h, 2 hours -zone.external.session_expiry_interval = 2h - -## Maximum queue length. Enqueued messages when persistent client disconnected, -## or inflight window is full. 0 means no limit. -## -## Value: Number >= 0 -zone.external.max_mqueue_len = 1000 - -## Topic priorities. -## 'none' to indicate no priority table (by default), hence all messages -## are treated equal -## -## Priority number [1-255] -## Example: topic/1=10,topic/2=8 -## NOTE: comma and equal signs are not allowed for priority topic names -## NOTE: messages for topics not in the priority table are treated as -## either highest or lowest priority depending on the configured -## value for mqueue_default_priority -## -zone.external.mqueue_priorities = none - -## Default to highest priority for topics not matching priority table -## -## Value: highest | lowest -zone.external.mqueue_default_priority = highest - -## Whether to enqueue QoS0 messages. -## -## Value: false | true -zone.external.mqueue_store_qos0 = true - -## Whether to turn on flapping detect -## -## Value: on | off -zone.external.enable_flapping_detect = off - -## Message limit for the a external MQTT connection. -## -## Value: Number,Duration -## Example: 100 messages per 10 seconds. -#zone.external.rate_limit.conn_messages_in = 100,10s - -## Bytes limit for a external MQTT connections. -## -## Value: Number,Duration -## Example: 100KB incoming per 10 seconds. -#zone.external.rate_limit.conn_bytes_in = 100KB,10s - -## Messages quota for the each of external MQTT connection. -## This value consumed by the number of recipient on a message. -## -## Value: Number, Duration -## -## Example: 100 messaegs per 1s -#zone.external.quota.conn_messages_routing = 100,1s - -## Messages quota for the all of external MQTT connections. -## This value consumed by the number of recipient on a message. -## -## Value: Number, Duration -## -## Example: 200000 messaegs per 1s -#zone.external.quota.overall_messages_routing = 200000,1s - -## All the topics will be prefixed with the mountpoint path if this option is enabled. -## -## Variables in mountpoint path: -## - %c: clientid -## - %u: username -## -## Value: String -## zone.external.mountpoint = devicebound/ - -## Whether use username replace client id -## -## Value: boolean -## Default: false -zone.external.use_username_as_clientid = false - -## Whether to ignore loop delivery of messages.(for mqtt v3.1.1) -## -## Value: true | false -zone.external.ignore_loop_deliver = false - -## Whether to parse the MQTT frame in strict mode -## -## Value: true | false -zone.external.strict_mode = false - -## Specify the response information returned to the client -## -## Value: String -## zone.external.response_information = example - -##-------------------------------------------------------------------- -## Internal Zone - -zone.internal.allow_anonymous = true - -## Enable per connection stats. -## -## Value: Flag -zone.internal.enable_stats = on - -## Enable ACL check. -## -## Value: Flag -zone.internal.enable_acl = off - -## The action when acl check reject current operation -## -## Value: ignore | disconnect -## Default: ignore -zone.internal.acl_deny_action = ignore - -## See zone.$name.force_gc_policy -## zone.internal.force_gc_policy = 128000|128MB - -## See zone.$name.wildcard_subscription. -## -## Value: boolean -## zone.internal.wildcard_subscription = true - -## See zone.$name.shared_subscription. -## -## Value: boolean -## zone.internal.shared_subscription = true - -## See zone.$name.max_subscriptions. -## -## Value: Integer -zone.internal.max_subscriptions = 0 - -## See zone.$name.max_inflight -## -## Value: Number -zone.internal.max_inflight = 128 - -## See zone.$name.max_awaiting_rel -## -## Value: Number -zone.internal.max_awaiting_rel = 1000 - -## See zone.$name.max_mqueue_len -## -## Value: Number >= 0 -zone.internal.max_mqueue_len = 10000 - -## Whether to enqueue Qos0 messages. -## -## Value: false | true -zone.internal.mqueue_store_qos0 = true - -## Whether to turn on flapping detect -## -## Value: on | off -zone.internal.enable_flapping_detect = off - -## See zone.$name.force_shutdown_policy -zone.internal.force_shutdown_policy = 128000|128MB - -## All the topics will be prefixed with the mountpoint path if this option is enabled. -## -## Variables in mountpoint path: -## - %c: clientid -## - %u: username -## -## Value: String -## zone.internal.mountpoint = cloudbound/ - -## Whether to ignore loop delivery of messages.(for mqtt v3.1.1) -## -## Value: true | false -zone.internal.ignore_loop_deliver = false - -## Whether to parse the MQTT frame in strict mode -## -## Value: true | false -zone.internal.strict_mode = false - -## Specify the response information returned to the client -## -## Value: String -## zone.internal.response_information = example - -## Allow the zone's clients to bypass authentication step -## -## Value: true | false -zone.internal.bypass_auth_plugins = true diff --git a/priv/emqx.schema b/priv/emqx.schema index 830a0e934..38fd9f9d2 100644 --- a/priv/emqx.schema +++ b/priv/emqx.schema @@ -689,6 +689,12 @@ end}. {datatype, {enum, [allow, deny]}} ]}. +%% @doc Default ACL file. +{mapping, "acl_file", "emqx.acl_file", [ + {datatype, string}, + hidden +]}. + %% @doc Enable ACL cache for publish. {mapping, "enable_acl_cache", "emqx.enable_acl_cache", [ {default, on}, @@ -1239,9 +1245,9 @@ end}. ]}. {mapping, "listener.tcp.$name.high_watermark", "emqx.listeners", [ - {datatype, bytesize}, - {default, "1MB"} -]}. + {datatype, bytesize}, + {default, "1MB"} + ]}. {mapping, "listener.tcp.$name.tune_buffer", "emqx.listeners", [ {datatype, flag}, @@ -1336,9 +1342,9 @@ end}. ]}. {mapping, "listener.ssl.$name.high_watermark", "emqx.listeners", [ - {datatype, bytesize}, - {default, "1MB"} -]}. + {datatype, bytesize}, + {default, "1MB"} + ]}. {mapping, "listener.ssl.$name.tune_buffer", "emqx.listeners", [ {datatype, flag}, From 71d02e80119ebde8db25f13a47c05a8132da98a6 Mon Sep 17 00:00:00 2001 From: Zaiming Shi Date: Fri, 19 Feb 2021 10:20:16 +0100 Subject: [PATCH 26/26] chore(emqx_connection): delete stale code --- src/emqx_connection.erl | 56 ----------------------------------------- 1 file changed, 56 deletions(-) diff --git a/src/emqx_connection.erl b/src/emqx_connection.erl index eb9a95dc4..11a3a9418 100644 --- a/src/emqx_connection.erl +++ b/src/emqx_connection.erl @@ -465,62 +465,6 @@ system_continue(Parent, _Debug, State) -> system_terminate(Reason, _Parent, _Debug, State) -> terminate(Reason, State). -system_code_change(State, _Mod, {down, Vsn}, _Extra) - when Vsn == "4.2.0"; - Vsn == "4.2.1" -> - Channel = State#state.channel, - NSerialize = emqx_frame:serialize_fun(State#state.serialize), - case {State#state.parse_state, element(10, Channel)} of - {{none, _}, undefined} -> - {ok, State#state{serialize = NSerialize}}; - {Ps, Quota} -> - %% BACKW: e4.2.0-e4.2.1 - %% We can't recover/reconstruct anonymous function state for - %% Parser or Quota consumer. So just close it. - ?LOG(error, "Unsupport downgrade connection ~0p, peername: ~0p." - " Due to it have an incomplete frame or unsafed quota counter," - " parser_state: ~0p, quota: ~0p." - " Force close it now!!!", [self(), State#state.peername, Ps, Quota]), - self() ! {close, unsupported_downgrade_connection_state}, - {ok, State#state{serialize = NSerialize}} - end; - -system_code_change(State, _Mod, Vsn, _Extra) - when Vsn == "4.2.0"; - Vsn == "4.2.1" -> - Channel = State#state.channel, - NChannel = - case element(10, Channel) of - undefined -> Channel; - Quoter -> - Zone = element(2, Quoter), - Cks = element(3, Quoter), - NCks = [case Name == overall_messages_routing of - true -> Ck#{consumer => Zone}; - _ -> Ck - end || Ck = #{name := Name} <- Cks], - setelement(10, Channel, setelement(3, Quoter, NCks)) - end, - - NParseState = - case State#state.parse_state of - Ps = {none, _} -> Ps; - Ps when is_function(Ps) -> - case erlang:fun_info(Ps, env) of - {_, [Hdr, Opts]} -> - {{len, #{hdr => Hdr, len => {1, 0}}}, Opts}; - {_, [Bin, Hdr, Len, Opts]} when is_binary(Bin) -> - {{body, #{hdr => Hdr, len => Len, rest => Bin}}, Opts}; - {_, [Hdr, Multip, Len, Opts]} -> - {{len, #{hdr => Hdr, len => {Multip, Len}}}, Opts} - end - end, - - {_, [Ver, MaxSize]} = erlang:fun_info(State#state.serialize, env), - NSerialize = #{version => Ver, max_size => MaxSize}, - - {ok, State#state{channel = NChannel, parse_state = NParseState, serialize = NSerialize}}; - system_code_change(State, _Mod, _OldVsn, _Extra) -> {ok, State}.