Merge pull request #3043 from emqx/master
Auto-pull-request-by-2019-11-18
This commit is contained in:
commit
8f5a0a8cbd
12
README-CN.md
12
README-CN.md
|
@ -7,6 +7,9 @@
|
||||||
[](https://slack-invite.emqx.io)
|
[](https://slack-invite.emqx.io)
|
||||||
[](https://twitter.com/emqtt)
|
[](https://twitter.com/emqtt)
|
||||||
|
|
||||||
|
|
||||||
|
[](https://emqx.io/cn/about/jobs)
|
||||||
|
|
||||||
[English](./README.md) | 简体中文
|
[English](./README.md) | 简体中文
|
||||||
|
|
||||||
*EMQ X* 是一款完全开源,高度可伸缩,高可用的分布式 MQTT 消息服务器,适用于 IoT、M2M 和移动应用程序,可处理千万级别的并发客户端。
|
*EMQ X* 是一款完全开源,高度可伸缩,高可用的分布式 MQTT 消息服务器,适用于 IoT、M2M 和移动应用程序,可处理千万级别的并发客户端。
|
||||||
|
@ -20,7 +23,13 @@
|
||||||
|
|
||||||
*EMQ X* 是跨平台的,支持 Linux、Unix、Mac OS 以及 Windows。这意味着 *EMQ X* 可以部署在 x86_64 架构的服务器上,也可以部署在 Raspberry Pi 这样的 ARM 设备上。
|
*EMQ X* 是跨平台的,支持 Linux、Unix、Mac OS 以及 Windows。这意味着 *EMQ X* 可以部署在 x86_64 架构的服务器上,也可以部署在 Raspberry Pi 这样的 ARM 设备上。
|
||||||
|
|
||||||
获取适合你的二进制软件包,[点此下载](https://emqx.io/downloads)。
|
**使用 EMQ X Docker 镜像安装**
|
||||||
|
|
||||||
|
```
|
||||||
|
docker run -d --name emqx -p 1883:1883 -p 8083:8083 -p 8883:8883 -p 8084:8084 -p 18083:18083 emqx/emqx
|
||||||
|
```
|
||||||
|
|
||||||
|
**或者 [点此下载](https://emqx.io/downloads) 适合你的二进制软件包**
|
||||||
|
|
||||||
- [单节点安装](https://docs.emqx.io/broker/v3/cn/install.html)
|
- [单节点安装](https://docs.emqx.io/broker/v3/cn/install.html)
|
||||||
- [集群安装](https://docs.emqx.io/broker/v3/cn/cluster.html)
|
- [集群安装](https://docs.emqx.io/broker/v3/cn/cluster.html)
|
||||||
|
@ -68,6 +77,7 @@ cd _rel/emqx && ./bin/emqx console
|
||||||
- [Twitter](https://twitter.com/emqtt)
|
- [Twitter](https://twitter.com/emqtt)
|
||||||
- [Forum](https://groups.google.com/d/forum/emqtt)
|
- [Forum](https://groups.google.com/d/forum/emqtt)
|
||||||
- [Blog](https://medium.com/@emqtt)
|
- [Blog](https://medium.com/@emqtt)
|
||||||
|
- [Reddit](https://www.reddit.com/r/emqx/)
|
||||||
|
|
||||||
欢迎你将任何 bug、问题和功能请求提交到 [emqx/emqx](https://github.com/emqx/emqx/issues)。
|
欢迎你将任何 bug、问题和功能请求提交到 [emqx/emqx](https://github.com/emqx/emqx/issues)。
|
||||||
|
|
||||||
|
|
12
README.md
12
README.md
|
@ -7,6 +7,9 @@
|
||||||
[](https://slack-invite.emqx.io)
|
[](https://slack-invite.emqx.io)
|
||||||
[](https://twitter.com/emqtt)
|
[](https://twitter.com/emqtt)
|
||||||
|
|
||||||
|
|
||||||
|
[](https://emqx.io/about/jobs)
|
||||||
|
|
||||||
English | [简体中文](./README-CN.md)
|
English | [简体中文](./README-CN.md)
|
||||||
|
|
||||||
*EMQ X* broker is a fully open source, highly scalable, highly available distributed MQTT messaging broker for IoT, M2M and Mobile applications that can handle tens of millions of concurrent clients.
|
*EMQ X* broker is a fully open source, highly scalable, highly available distributed MQTT messaging broker for IoT, M2M and Mobile applications that can handle tens of millions of concurrent clients.
|
||||||
|
@ -20,7 +23,13 @@ Starting from 3.0 release, *EMQ X* broker fully supports MQTT V5.0 protocol spec
|
||||||
|
|
||||||
The *EMQ X* broker is cross-platform, which supports Linux, Unix, Mac OS and Windows. It means *EMQ X* can be deployed on x86_64 architecture servers and ARM devices like Raspberry Pi.
|
The *EMQ X* broker is cross-platform, which supports Linux, Unix, Mac OS and Windows. It means *EMQ X* can be deployed on x86_64 architecture servers and ARM devices like Raspberry Pi.
|
||||||
|
|
||||||
Download the binary package for your platform from [here](https://emqx.io/downloads).
|
**Installing via EMQ X Docker Image**
|
||||||
|
|
||||||
|
```
|
||||||
|
docker run -d --name emqx -p 1883:1883 -p 8083:8083 -p 8883:8883 -p 8084:8084 -p 18083:18083 emqx/emqx
|
||||||
|
```
|
||||||
|
|
||||||
|
**Or download the binary package for your platform from [here](https://emqx.io/downloads).**
|
||||||
|
|
||||||
- [Single Node Install](https://docs.emqx.io/broker/v3/en/install.html)
|
- [Single Node Install](https://docs.emqx.io/broker/v3/en/install.html)
|
||||||
- [Multi Node Install](https://docs.emqx.io/broker/v3/en/cluster.html)
|
- [Multi Node Install](https://docs.emqx.io/broker/v3/en/cluster.html)
|
||||||
|
@ -69,6 +78,7 @@ You can reach the EMQ community and developers via the following channels:
|
||||||
- [Twitter](https://twitter.com/emqtt)
|
- [Twitter](https://twitter.com/emqtt)
|
||||||
- [Forum](https://groups.google.com/d/forum/emqtt)
|
- [Forum](https://groups.google.com/d/forum/emqtt)
|
||||||
- [Blog](https://medium.com/@emqtt)
|
- [Blog](https://medium.com/@emqtt)
|
||||||
|
- [Reddit](https://www.reddit.com/r/emqx/)
|
||||||
|
|
||||||
Please submit any bugs, issues, and feature requests to [emqx/emqx](https://github.com/emqx/emqx/issues).
|
Please submit any bugs, issues, and feature requests to [emqx/emqx](https://github.com/emqx/emqx/issues).
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,18 @@
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
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
|
||||||
|
gLxgEkY=
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
MIIC6jCCAdKgAwIBAgIBATANBgkqhkiG9w0BAQsFADATMREwDwYDVQQDDAhNeVRl
|
MIIC5zCCAc+gAwIBAgIBATANBgkqhkiG9w0BAQsFADATMREwDwYDVQQDDAhNeVRl
|
||||||
c3RDQTAeFw0xNzExMDIxMzQyNDhaFw0xOTExMDIxMzQyNDhaMC0xGjAYBgNVBAMT
|
c3RDQTAeFw0xOTExMTUwNzI2NThaFw0yMTExMTQwNzI2NThaMCoxFzAVBgNVBAMM
|
||||||
EU1hY0Jvb2stQWlyLmxvY2FsMQ8wDQYDVQQKEwZzZXJ2ZXIwggEiMA0GCSqGSIb3
|
DjAwMDQubm92YWxvY2FsMQ8wDQYDVQQKDAZzZXJ2ZXIwggEiMA0GCSqGSIb3DQEB
|
||||||
DQEBAQUAA4IBDwAwggEKAoIBAQDUO/kL3ar3WsopPF12qAf+cwDHklGJIxJsjdoZ
|
AQUAA4IBDwAwggEKAoIBAQDC5JE48PJ/BFTLEseEbrGIdYB6w29hme4KFKmAqlLQ
|
||||||
XgI1lPEe1W1QXwb/G/tyf6Fj2J8CD5bfsRjDxAemFIBVrFwlunCk+Gs6xR7vzz4O
|
kpwwZJAsm/9iuXy6svJf7Tzzc173Jkgzw7DzhzSf1VgRDrOCQS+IU6s8UXfUMJt/
|
||||||
Fonoj4pmleruLQrNY/bHa2WN97OdISyXzhOgDwSaqobnF0n/f0Mx+9sdHO3p8LNB
|
AmP1SkU2mUJ/+pnEGRKtVkF9LCScinI95Iwt3xngdjMYXwk+S9Le3/8782ClBwZG
|
||||||
3JXUyBpwDNr/TTfAb4pbQEu3LF4p7uyd1eLhKzUxSiWzKtjB1EYObA87fZu0tBJZ
|
vffXQ7hd5HnShgyqFVePgrKmr879NTylfvAWPwux2kdXNnbOHIrhcZm0NeMNf7hs
|
||||||
iGujuFiI7tf4qWKeuAoRa/cXkgVZhk0utYauDoa7qBZ5O6ZdEko9ov0+i5+1JGU/
|
UNURFlqo4rA0FV9dIHMryPkM7ygoaMog2XmcCnq/jf/MfPTQPYjQ9iLPOGrYi0pY
|
||||||
w5wrSPNAnM2lYVUn0kJmcV2gwa4RZFjdqp+/Fx+HnKbnhZEnAgMBAAGjLzAtMAkG
|
X12uFb55duRGsvs7MIkNc8fn2VERoC69QX+GK+zAUGZ/AgMBAAGjLzAtMAkGA1Ud
|
||||||
A1UdEwQCMAAwCwYDVR0PBAQDAgUgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA0GCSqG
|
EwQCMAAwCwYDVR0PBAQDAgUgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA0GCSqGSIb3
|
||||||
SIb3DQEBCwUAA4IBAQByWhNxX/L5QYBiMY4JM1RRciV4uI3F2vsc0yMFDSrZza+5
|
DQEBCwUAA4IBAQBpW7Ge5duo6/u3xIl0XhG/2dlSwlUUpO3Ecc13gmh44nJR66VH
|
||||||
tNJQS86hjQsCRZh9VshezvT7k1yVsAC4pnu2pzob8H3KG4vYBafMdl2Ghgv3RMix
|
BEiimsol6gIgcSTk4pVY1DLb/09Nwv0TILl3Dc4QtXhM4gIlNRR79mLVsnPTef5e
|
||||||
J3NrBhcoYYhXEoZHost+htxEi7P3QBo/qDkk48/d30+aDPbms6kQd8Fj8+C5tD3b
|
xkmesQaLihSCroHq8bONnO/Xgj5hCg8uI4j3vHtOikjABxQPOrCfc2uSrenU7aol
|
||||||
aznO5Qlni72uTaM7fNA8exoc/YZc83lsqv7v+UzNQR595jnYSIAZcgil1qqygOan
|
1HBijCY6R+pg6WxBOZ2Teiaoxjn78IxSKLXW0pLRJIPpet1hefR0sKkmPfVGyg8H
|
||||||
Zx/RsMGUz6EYI9lPpoyyVtw13SoQshfgwvUlvBMiekSuI/pp6N7QPK6C8DLO0tVv
|
g7hqo+Houw8PQf2HLZnU656vyTlgIh6ES1x7Plb0cIw/LGr4rMkXs+DFg9SLbetT
|
||||||
gXJjDgioqHc3hcgG4cskLbfVnohiwdhQTFayrLEk
|
ncT4plfucsek7ImN9Dw2w2hM2FZwB8ycZfmu
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
MIIC6jCCAdKgAwIBAgIBAjANBgkqhkiG9w0BAQsFADATMREwDwYDVQQDDAhNeVRl
|
MIIC5zCCAc+gAwIBAgIBAjANBgkqhkiG9w0BAQsFADATMREwDwYDVQQDDAhNeVRl
|
||||||
c3RDQTAeFw0xNzExMDIxMzQyNDhaFw0xOTExMDIxMzQyNDhaMC0xGjAYBgNVBAMT
|
c3RDQTAeFw0xOTExMTUwNzI2NThaFw0yMTExMTQwNzI2NThaMCoxFzAVBgNVBAMM
|
||||||
EU1hY0Jvb2stQWlyLmxvY2FsMQ8wDQYDVQQKEwZjbGllbnQwggEiMA0GCSqGSIb3
|
DjAwMDQubm92YWxvY2FsMQ8wDQYDVQQKDAZjbGllbnQwggEiMA0GCSqGSIb3DQEB
|
||||||
DQEBAQUAA4IBDwAwggEKAoIBAQC8GptpL25hv1Qa3jCn4VLvDRH/SrHg9wXvqRkz
|
AQUAA4IBDwAwggEKAoIBAQDcwo5SaoRpzkqy+Y9OADOL7U84h1VFfjb5Uu5raenO
|
||||||
HuiKMxYT30m4+kcaXv350CJrkV+8lR24wdN7DBVewpCUnyUBbzkLccy1LUzunZ3z
|
elmHSaCZpVP2EsDUaWavtabHd9fa5Oq6lOyZPDZM6xttfi78EV4RRfEJ4XdvE54W
|
||||||
nm37j6cautD3rlC9gsC9d0uJ745FLx5t/6f1jMk9rWxn+4iSGAnkWC3mVaQxP1zQ
|
MZSDAGz4RwxfGOQWBSFyp1NrzT32eqeDSyBrE3jhWx9UUUMwthg5YYjCdBwK+Dwf
|
||||||
q8GI97uob9HNb0OH6ygHJAcKOWB+85a29LIMa1uo/lT3hMr8sBg2vX+1F/gTusmW
|
hsfS1YeAfXPNO/BGSTe0dPhjLztXe1BkFO5VAwkSXaPs2lBJddOgpTTLXQ3+hIPL
|
||||||
xVoQc9XJxBCs995qsH0UkZIuOY0XZp9/qFfcZv2QmslG8DojIIHKcujzu8bItE2M
|
ozkiaTOMOvIMXsCspdhJbSc+jAAGZT5X9Tx7htYbPXIwyDJgeYGmLtr9XxPJ8XGR
|
||||||
OyL5NlWLvN6qg59hHzF4+D+T+8GkhhKWSC+xdY14eQ5fB4S5AgMBAAGjLzAtMAkG
|
rpxkB3zASRcwQzsxTcwkG4E32T53tKsljTkNt15rIoo3AgMBAAGjLzAtMAkGA1Ud
|
||||||
A1UdEwQCMAAwCwYDVR0PBAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMCMA0GCSqG
|
EwQCMAAwCwYDVR0PBAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMCMA0GCSqGSIb3
|
||||||
SIb3DQEBCwUAA4IBAQBLV4ZfhiKiFVnL/xO0MRGSKr3xd0LK64SW8Iw5DYkc0jNX
|
DQEBCwUAA4IBAQBRtQMvUmiB84RmrGwHCP8hcGUWTz03mtTjGrykNA7YQkA09cRl
|
||||||
sDrRbj2I/KJ/Rc4AeKT751L+C+KBzYpFgiLrxDmt/5pmgiFH51hPQtL7kRC0z2NY
|
RwiqYMWh6zHjdX1Ri3m9eIi/QSK/JX3S9zjZU9dSTtsdnMhkRL08kcxauv9gVXCG
|
||||||
EY/P+u4IFVSo+b1hHYU7y+OMj6/Vvd4x0ETS4rHWI4mPDfGfvClEVLOktgRKrMU5
|
G1Vf+lUVJxTqwuAmcLiDNg9/89sSlxQXFS7Jn9TwTvNiRoFoN5IiJ4LsXyr4uS9Y
|
||||||
9aTltF4U0FBUlYZTQBNBUFwBzj1+0lxK4EdhRmmWJ+uW9rgkQxpnUdbCPGvUKFRp
|
S4Ul1aqetwpTV8bjpIbRJbOR8qBFshIZOPdgAT3RqbD/vpGzOvvV0c9g3VFLYoK3
|
||||||
3AbdHBAU9H2zVd2VZoJu6r7LMp6agxu0rYLgmamRAt+8rnDXvy7H1ZNdjT6fTbUO
|
nQ63w1zhwYxC4MQD9rN7JRAKCDQBLNzf8PW0RSG9pVsf1IjaLxtsmQMgrAati/Ux
|
||||||
omVBMyJAc1+10gjpHw/EUD58t5/I5tZrnrANPgIs
|
AG76LAn9sodtb4GtV8E9ITG0pMNlJyUovstS
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
|
|
|
@ -1,27 +1,27 @@
|
||||||
-----BEGIN RSA PRIVATE KEY-----
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
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
|
||||||
-----END RSA PRIVATE KEY-----
|
-----END RSA PRIVATE KEY-----
|
||||||
|
|
|
@ -1,27 +1,27 @@
|
||||||
-----BEGIN RSA PRIVATE KEY-----
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
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
|
||||||
-----END RSA PRIVATE KEY-----
|
-----END RSA PRIVATE KEY-----
|
||||||
|
|
|
@ -417,7 +417,7 @@ log.file = emqx.log
|
||||||
##
|
##
|
||||||
## Value: Number
|
## Value: Number
|
||||||
## Default: 10M
|
## Default: 10M
|
||||||
## Supported Unit: KB | MB | G
|
## Supported Unit: KB | MB | GB
|
||||||
log.rotation.size = 10MB
|
log.rotation.size = 10MB
|
||||||
|
|
||||||
## Maximum rotation count of log files.
|
## Maximum rotation count of log files.
|
||||||
|
|
|
@ -154,15 +154,13 @@
|
||||||
%% Banned
|
%% Banned
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
-type(banned_who() :: {clientid, binary()}
|
|
||||||
| {username, binary()}
|
|
||||||
| {ip_address, inet:ip_address()}).
|
|
||||||
|
|
||||||
-record(banned, {
|
-record(banned, {
|
||||||
who :: banned_who(),
|
who :: {clientid, binary()}
|
||||||
reason :: binary(),
|
| {username, binary()}
|
||||||
|
| {ip_address, inet:ip_address()},
|
||||||
by :: binary(),
|
by :: binary(),
|
||||||
desc :: binary(),
|
reason :: binary(),
|
||||||
|
at :: integer(),
|
||||||
until :: integer()
|
until :: integer()
|
||||||
}).
|
}).
|
||||||
|
|
||||||
|
|
12
rebar.config
12
rebar.config
|
@ -1,9 +1,11 @@
|
||||||
|
{minimum_otp_vsn, "21.0"}.
|
||||||
|
|
||||||
{deps,
|
{deps,
|
||||||
[{jsx, "2.9.0"}, % hex
|
[{jsx, "2.10.0"},
|
||||||
{cowboy, "2.6.1"}, % hex
|
{cowboy, "2.7.0"},
|
||||||
{gproc, "0.8.0"}, % hex
|
{gproc, "0.8.0"},
|
||||||
{esockd, {git, "https://github.com/emqx/esockd", {tag, "v5.5.2"}}},
|
{esockd, {git, "https://github.com/emqx/esockd", {tag, "5.6.0"}}},
|
||||||
{ekka, {git, "https://github.com/emqx/ekka", {tag, "v0.6.2"}}},
|
{ekka, {git, "https://github.com/emqx/ekka", {tag, "0.7.1"}}},
|
||||||
{gen_rpc, {git, "https://github.com/emqx/gen_rpc", {tag, "2.4.1"}}},
|
{gen_rpc, {git, "https://github.com/emqx/gen_rpc", {tag, "2.4.1"}}},
|
||||||
{cuttlefish, {git, "https://github.com/emqx/cuttlefish", {tag, "v3.0.0"}}}
|
{cuttlefish, {git, "https://github.com/emqx/cuttlefish", {tag, "v3.0.0"}}}
|
||||||
]}.
|
]}.
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
-export([start_link/0, stop/0]).
|
-export([start_link/0, stop/0]).
|
||||||
|
|
||||||
-export([ check/1
|
-export([ check/1
|
||||||
, add/1
|
, create/1
|
||||||
, delete/1
|
, delete/1
|
||||||
, info/1
|
, info/1
|
||||||
]).
|
]).
|
||||||
|
@ -74,21 +74,39 @@ start_link() ->
|
||||||
stop() -> gen_server:stop(?MODULE).
|
stop() -> gen_server:stop(?MODULE).
|
||||||
|
|
||||||
-spec(check(emqx_types:clientinfo()) -> boolean()).
|
-spec(check(emqx_types:clientinfo()) -> boolean()).
|
||||||
check(#{clientid := ClientId,
|
check(ClientInfo) ->
|
||||||
username := Username,
|
do_check({clientid, maps:get(clientid, ClientInfo, undefined)})
|
||||||
peerhost := IPAddr}) ->
|
orelse do_check({username, maps:get(username, ClientInfo, undefined)})
|
||||||
ets:member(?BANNED_TAB, {clientid, ClientId})
|
orelse do_check({peerhost, maps:get(peerhost, ClientInfo, undefined)}).
|
||||||
orelse ets:member(?BANNED_TAB, {username, Username})
|
|
||||||
orelse ets:member(?BANNED_TAB, {ipaddr, IPAddr}).
|
|
||||||
|
|
||||||
-spec(add(emqx_types:banned()) -> ok).
|
do_check({_, undefined}) ->
|
||||||
add(Banned) when is_record(Banned, banned) ->
|
false;
|
||||||
|
do_check(Who) when is_tuple(Who) ->
|
||||||
|
case mnesia:dirty_read(?BANNED_TAB, Who) of
|
||||||
|
[] -> false;
|
||||||
|
[#banned{until = Until}] ->
|
||||||
|
Until > erlang:system_time(millisecond)
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec(create(emqx_types:banned()) -> ok).
|
||||||
|
create(#{who := Who,
|
||||||
|
by := By,
|
||||||
|
reason := Reason,
|
||||||
|
at := At,
|
||||||
|
until := Until}) ->
|
||||||
|
mnesia:dirty_write(?BANNED_TAB, #banned{who = Who,
|
||||||
|
by = By,
|
||||||
|
reason = Reason,
|
||||||
|
at = At,
|
||||||
|
until = Until});
|
||||||
|
create(Banned) when is_record(Banned, banned) ->
|
||||||
mnesia:dirty_write(?BANNED_TAB, Banned).
|
mnesia:dirty_write(?BANNED_TAB, Banned).
|
||||||
|
|
||||||
-spec(delete({clientid, emqx_types:clientid()}
|
-spec(delete({clientid, emqx_types:clientid()}
|
||||||
| {username, emqx_types:username()}
|
| {username, emqx_types:username()}
|
||||||
| {peerhost, emqx_types:peerhost()}) -> ok).
|
| {peerhost, emqx_types:peerhost()}) -> ok).
|
||||||
delete(Key) -> mnesia:dirty_delete(?BANNED_TAB, Key).
|
delete(Who) ->
|
||||||
|
mnesia:dirty_delete(?BANNED_TAB, Who).
|
||||||
|
|
||||||
info(InfoKey) ->
|
info(InfoKey) ->
|
||||||
mnesia:table_info(?BANNED_TAB, InfoKey).
|
mnesia:table_info(?BANNED_TAB, InfoKey).
|
||||||
|
|
|
@ -219,7 +219,6 @@ handle_in(?CONNECT_PACKET(ConnPkt), Channel) ->
|
||||||
fun enrich_client/2,
|
fun enrich_client/2,
|
||||||
fun set_logger_meta/2,
|
fun set_logger_meta/2,
|
||||||
fun check_banned/2,
|
fun check_banned/2,
|
||||||
fun check_flapping/2,
|
|
||||||
fun auth_connect/2], ConnPkt, Channel) of
|
fun auth_connect/2], ConnPkt, Channel) of
|
||||||
{ok, NConnPkt, NChannel} ->
|
{ok, NConnPkt, NChannel} ->
|
||||||
process_connect(NConnPkt, NChannel);
|
process_connect(NConnPkt, NChannel);
|
||||||
|
@ -942,7 +941,7 @@ set_logger_meta(_ConnPkt, #channel{clientinfo = #{clientid := ClientId}}) ->
|
||||||
emqx_logger:set_metadata_clientid(ClientId).
|
emqx_logger:set_metadata_clientid(ClientId).
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
%% Check banned/flapping
|
%% Check banned
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
check_banned(_ConnPkt, #channel{clientinfo = ClientInfo = #{zone := Zone}}) ->
|
check_banned(_ConnPkt, #channel{clientinfo = ClientInfo = #{zone := Zone}}) ->
|
||||||
|
@ -951,13 +950,6 @@ check_banned(_ConnPkt, #channel{clientinfo = ClientInfo = #{zone := Zone}}) ->
|
||||||
false -> ok
|
false -> ok
|
||||||
end.
|
end.
|
||||||
|
|
||||||
check_flapping(_ConnPkt, #channel{clientinfo = ClientInfo = #{zone := Zone}}) ->
|
|
||||||
case emqx_zone:enable_flapping_detect(Zone)
|
|
||||||
andalso emqx_flapping:check(ClientInfo) of
|
|
||||||
true -> {error, ?RC_CONNECTION_RATE_EXCEEDED};
|
|
||||||
false -> ok
|
|
||||||
end.
|
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
%% Auth Connect
|
%% Auth Connect
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
|
@ -101,7 +101,8 @@
|
||||||
-spec(start_link(esockd:transport(), esockd:socket(), proplists:proplist())
|
-spec(start_link(esockd:transport(), esockd:socket(), proplists:proplist())
|
||||||
-> {ok, pid()}).
|
-> {ok, pid()}).
|
||||||
start_link(Transport, Socket, Options) ->
|
start_link(Transport, Socket, Options) ->
|
||||||
CPid = proc_lib:spawn_link(?MODULE, init, [self(), Transport, Socket, Options]),
|
Args = [self(), Transport, Socket, Options],
|
||||||
|
CPid = proc_lib:spawn_link(?MODULE, init, Args),
|
||||||
{ok, CPid}.
|
{ok, CPid}.
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
@ -196,7 +197,7 @@ do_init(Parent, Transport, Socket, Options) ->
|
||||||
IdleTimeout = emqx_zone:idle_timeout(Zone),
|
IdleTimeout = emqx_zone:idle_timeout(Zone),
|
||||||
IdleTimer = start_timer(IdleTimeout, idle_timeout),
|
IdleTimer = start_timer(IdleTimeout, idle_timeout),
|
||||||
emqx_misc:tune_heap_size(emqx_zone:oom_policy(Zone)),
|
emqx_misc:tune_heap_size(emqx_zone:oom_policy(Zone)),
|
||||||
emqx_logger:set_metadata_peername(esockd_net:format(Peername)),
|
emqx_logger:set_metadata_peername(esockd:format(Peername)),
|
||||||
State = #state{transport = Transport,
|
State = #state{transport = Transport,
|
||||||
socket = Socket,
|
socket = Socket,
|
||||||
peername = Peername,
|
peername = Peername,
|
||||||
|
@ -601,7 +602,7 @@ handle_info(Info, State = #state{channel = Channel}) ->
|
||||||
%% Ensure rate limit
|
%% Ensure rate limit
|
||||||
|
|
||||||
ensure_rate_limit(Stats, State = #state{limiter = Limiter}) ->
|
ensure_rate_limit(Stats, State = #state{limiter = Limiter}) ->
|
||||||
case ?ENABLED(limiter) andalso emqx_limiter:check(Stats, Limiter) of
|
case ?ENABLED(Limiter) andalso emqx_limiter:check(Stats, Limiter) of
|
||||||
false -> State;
|
false -> State;
|
||||||
{ok, Limiter1} ->
|
{ok, Limiter1} ->
|
||||||
State#state{limiter = Limiter1};
|
State#state{limiter = Limiter1};
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
-export([start_link/0, stop/0]).
|
-export([start_link/0, stop/0]).
|
||||||
|
|
||||||
%% API
|
%% API
|
||||||
-export([check/1, detect/1]).
|
-export([detect/1]).
|
||||||
|
|
||||||
%% gen_server callbacks
|
%% gen_server callbacks
|
||||||
-export([ init/1
|
-export([ init/1
|
||||||
|
@ -54,8 +54,7 @@
|
||||||
clientid :: emqx_types:clientid(),
|
clientid :: emqx_types:clientid(),
|
||||||
peerhost :: emqx_types:peerhost(),
|
peerhost :: emqx_types:peerhost(),
|
||||||
started_at :: pos_integer(),
|
started_at :: pos_integer(),
|
||||||
detect_cnt :: pos_integer(),
|
detect_cnt :: pos_integer()
|
||||||
banned_at :: pos_integer()
|
|
||||||
}).
|
}).
|
||||||
|
|
||||||
-opaque(flapping() :: #flapping{}).
|
-opaque(flapping() :: #flapping{}).
|
||||||
|
@ -68,27 +67,14 @@ start_link() ->
|
||||||
|
|
||||||
stop() -> gen_server:stop(?MODULE).
|
stop() -> gen_server:stop(?MODULE).
|
||||||
|
|
||||||
%% @doc Check flapping when a MQTT client connected.
|
|
||||||
-spec(check(emqx_types:clientinfo()) -> boolean()).
|
|
||||||
check(#{clientid := ClientId}) ->
|
|
||||||
check(ClientId, get_policy()).
|
|
||||||
|
|
||||||
check(ClientId, #{banned_interval := Interval}) ->
|
|
||||||
case ets:lookup(?FLAPPING_TAB, {banned, ClientId}) of
|
|
||||||
[] -> false;
|
|
||||||
[#flapping{banned_at = BannedAt}] ->
|
|
||||||
now_diff(BannedAt) < Interval
|
|
||||||
end.
|
|
||||||
|
|
||||||
%% @doc Detect flapping when a MQTT client disconnected.
|
%% @doc Detect flapping when a MQTT client disconnected.
|
||||||
-spec(detect(emqx_types:clientinfo()) -> boolean()).
|
-spec(detect(emqx_types:clientinfo()) -> boolean()).
|
||||||
detect(Client) -> detect(Client, get_policy()).
|
detect(Client) -> detect(Client, get_policy()).
|
||||||
|
|
||||||
detect(#{clientid := ClientId, peerhost := PeerHost},
|
detect(#{clientid := ClientId, peerhost := PeerHost}, Policy = #{threshold := Threshold}) ->
|
||||||
Policy = #{threshold := Threshold}) ->
|
|
||||||
try ets:update_counter(?FLAPPING_TAB, ClientId, {#flapping.detect_cnt, 1}) of
|
try ets:update_counter(?FLAPPING_TAB, ClientId, {#flapping.detect_cnt, 1}) of
|
||||||
Cnt when Cnt < Threshold -> false;
|
Cnt when Cnt < Threshold -> false;
|
||||||
_Cnt -> case ets:lookup(?FLAPPING_TAB, ClientId) of
|
_Cnt -> case ets:take(?FLAPPING_TAB, ClientId) of
|
||||||
[Flapping] ->
|
[Flapping] ->
|
||||||
ok = gen_server:cast(?MODULE, {detected, Flapping, Policy}),
|
ok = gen_server:cast(?MODULE, {detected, Flapping, Policy}),
|
||||||
true;
|
true;
|
||||||
|
@ -118,52 +104,44 @@ now_diff(TS) -> erlang:system_time(millisecond) - TS.
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
init([]) ->
|
init([]) ->
|
||||||
#{duration := Duration, banned_interval := Interval} = get_policy(),
|
|
||||||
ok = emqx_tables:new(?FLAPPING_TAB, [public, set,
|
ok = emqx_tables:new(?FLAPPING_TAB, [public, set,
|
||||||
{keypos, 2},
|
{keypos, 2},
|
||||||
{read_concurrency, true},
|
{read_concurrency, true},
|
||||||
{write_concurrency, true}
|
{write_concurrency, true}
|
||||||
]),
|
]),
|
||||||
State = #{time => max(Duration, Interval) + 1, tref => undefined},
|
{ok, #{}, hibernate}.
|
||||||
{ok, ensure_timer(State), hibernate}.
|
|
||||||
|
|
||||||
handle_call(Req, _From, State) ->
|
handle_call(Req, _From, State) ->
|
||||||
?LOG(error, "Unexpected call: ~p", [Req]),
|
?LOG(error, "Unexpected call: ~p", [Req]),
|
||||||
{reply, ignored, State}.
|
{reply, ignored, State}.
|
||||||
|
|
||||||
handle_cast({detected, Flapping = #flapping{clientid = ClientId,
|
handle_cast({detected, #flapping{clientid = ClientId,
|
||||||
peerhost = PeerHost,
|
peerhost = PeerHost,
|
||||||
started_at = StartedAt,
|
started_at = StartedAt,
|
||||||
detect_cnt = DetectCnt},
|
detect_cnt = DetectCnt},
|
||||||
#{duration := Duration}}, State) ->
|
#{duration := Duration, banned_interval := Interval}}, State) ->
|
||||||
case (Interval = now_diff(StartedAt)) < Duration of
|
case now_diff(StartedAt) < Duration of
|
||||||
true -> %% Flapping happened:(
|
true -> %% Flapping happened:(
|
||||||
%% Log first
|
|
||||||
?LOG(error, "Flapping detected: ~s(~s) disconnected ~w times in ~wms",
|
?LOG(error, "Flapping detected: ~s(~s) disconnected ~w times in ~wms",
|
||||||
[ClientId, esockd_net:ntoa(PeerHost), DetectCnt, Duration]),
|
[ClientId, inet:ntoa(PeerHost), DetectCnt, Duration]),
|
||||||
%% Banned.
|
Now = erlang:system_time(millisecond),
|
||||||
BannedFlapping = Flapping#flapping{clientid = {banned, ClientId},
|
Banned = #banned{who = {clientid, ClientId},
|
||||||
banned_at = erlang:system_time(millisecond)
|
by = <<"flapping detector">>,
|
||||||
},
|
reason = <<"flapping is detected">>,
|
||||||
alarm_handler:set_alarm({{flapping_detected, ClientId}, BannedFlapping}),
|
at = Now,
|
||||||
ets:insert(?FLAPPING_TAB, BannedFlapping);
|
until = Now + Interval},
|
||||||
|
alarm_handler:set_alarm({{flapping_detected, ClientId}, Banned}),
|
||||||
|
emqx_banned:create(Banned);
|
||||||
false ->
|
false ->
|
||||||
?LOG(warning, "~s(~s) disconnected ~w times in ~wms",
|
?LOG(warning, "~s(~s) disconnected ~w times in ~wms",
|
||||||
[ClientId, esockd_net:ntoa(PeerHost), DetectCnt, Interval])
|
[ClientId, inet:ntoa(PeerHost), DetectCnt, Interval])
|
||||||
end,
|
end,
|
||||||
ets:delete_object(?FLAPPING_TAB, Flapping),
|
|
||||||
{noreply, State};
|
{noreply, State};
|
||||||
|
|
||||||
handle_cast(Msg, State) ->
|
handle_cast(Msg, State) ->
|
||||||
?LOG(error, "Unexpected cast: ~p", [Msg]),
|
?LOG(error, "Unexpected cast: ~p", [Msg]),
|
||||||
{noreply, State}.
|
{noreply, State}.
|
||||||
|
|
||||||
handle_info({timeout, TRef, expire_flapping}, State = #{tref := TRef}) ->
|
|
||||||
with_flapping_tab(fun expire_flapping/2,
|
|
||||||
[erlang:system_time(millisecond),
|
|
||||||
get_policy()]),
|
|
||||||
{noreply, ensure_timer(State#{tref => undefined}), hibernate};
|
|
||||||
|
|
||||||
handle_info(Info, State) ->
|
handle_info(Info, State) ->
|
||||||
?LOG(error, "Unexpected info: ~p", [Info]),
|
?LOG(error, "Unexpected info: ~p", [Info]),
|
||||||
{noreply, State}.
|
{noreply, State}.
|
||||||
|
@ -173,34 +151,3 @@ terminate(_Reason, _State) ->
|
||||||
|
|
||||||
code_change(_OldVsn, State, _Extra) ->
|
code_change(_OldVsn, State, _Extra) ->
|
||||||
{ok, State}.
|
{ok, State}.
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
%% Internal functions
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
|
|
||||||
ensure_timer(State = #{time := Time, tref := undefined}) ->
|
|
||||||
State#{tref => emqx_misc:start_timer(Time, expire_flapping)};
|
|
||||||
ensure_timer(State) -> State.
|
|
||||||
|
|
||||||
with_flapping_tab(Fun, Args) ->
|
|
||||||
case ets:info(?FLAPPING_TAB, size) of
|
|
||||||
undefined -> ok;
|
|
||||||
0 -> ok;
|
|
||||||
_Size -> erlang:apply(Fun, Args)
|
|
||||||
end.
|
|
||||||
|
|
||||||
expire_flapping(NowTime, #{duration := Duration, banned_interval := Interval}) ->
|
|
||||||
case ets:select(?FLAPPING_TAB,
|
|
||||||
[{#flapping{started_at = '$1', banned_at = undefined, _ = '_'},
|
|
||||||
[{'<', '$1', NowTime-Duration}], ['$_']},
|
|
||||||
{#flapping{clientid = {banned, '_'}, banned_at = '$1', _ = '_'},
|
|
||||||
[{'<', '$1', NowTime-Interval}], ['$_']}]) of
|
|
||||||
[] -> ok;
|
|
||||||
Flappings ->
|
|
||||||
lists:foreach(fun(Flapping = #flapping{clientid = {banned, ClientId}}) ->
|
|
||||||
ets:delete_object(?FLAPPING_TAB, Flapping),
|
|
||||||
alarm_handler:clear_alarm({flapping_detected, ClientId});
|
|
||||||
(_) -> ok
|
|
||||||
end, Flappings)
|
|
||||||
end.
|
|
||||||
|
|
||||||
|
|
|
@ -89,12 +89,12 @@ parse(<<>>, {none, Options}) ->
|
||||||
parse(<<Type:4, Dup:1, QoS:2, Retain:1, Rest/binary>>,
|
parse(<<Type:4, Dup:1, QoS:2, Retain:1, Rest/binary>>,
|
||||||
{none, Options = #{strict_mode := StrictMode}}) ->
|
{none, Options = #{strict_mode := StrictMode}}) ->
|
||||||
%% Validate header if strict mode.
|
%% Validate header if strict mode.
|
||||||
|
StrictMode andalso validate_header(Type, Dup, QoS, Retain),
|
||||||
Header = #mqtt_packet_header{type = Type,
|
Header = #mqtt_packet_header{type = Type,
|
||||||
dup = bool(Dup),
|
dup = bool(Dup),
|
||||||
qos = QoS,
|
qos = QoS,
|
||||||
retain = bool(Retain)
|
retain = bool(Retain)
|
||||||
},
|
},
|
||||||
StrictMode andalso validate_header(Type, Dup, QoS, Retain),
|
|
||||||
Header1 = case fixqos(Type, QoS) of
|
Header1 = case fixqos(Type, QoS) of
|
||||||
QoS -> Header;
|
QoS -> Header;
|
||||||
FixedQoS -> Header#mqtt_packet_header{qos = FixedQoS}
|
FixedQoS -> Header#mqtt_packet_header{qos = FixedQoS}
|
||||||
|
@ -164,7 +164,8 @@ packet(Header, Variable, Payload) ->
|
||||||
parse_packet(#mqtt_packet_header{type = ?CONNECT}, FrameBin, _Options) ->
|
parse_packet(#mqtt_packet_header{type = ?CONNECT}, FrameBin, _Options) ->
|
||||||
{ProtoName, Rest} = parse_utf8_string(FrameBin),
|
{ProtoName, Rest} = parse_utf8_string(FrameBin),
|
||||||
<<BridgeTag:4, ProtoVer:4, Rest1/binary>> = Rest,
|
<<BridgeTag:4, ProtoVer:4, Rest1/binary>> = Rest,
|
||||||
% Note: Crash when reserved flag doesn't equal to 0, there is no strict compliance with the MQTT5.0.
|
% Note: Crash when reserved flag doesn't equal to 0, there is no strict
|
||||||
|
% compliance with the MQTT5.0.
|
||||||
<<UsernameFlag : 1,
|
<<UsernameFlag : 1,
|
||||||
PasswordFlag : 1,
|
PasswordFlag : 1,
|
||||||
WillRetain : 1,
|
WillRetain : 1,
|
||||||
|
@ -201,13 +202,15 @@ parse_packet(#mqtt_packet_header{type = ?CONNACK},
|
||||||
properties = Properties
|
properties = Properties
|
||||||
};
|
};
|
||||||
|
|
||||||
parse_packet(#mqtt_packet_header{type = ?PUBLISH, qos = QoS}, Bin, #{version := Ver}) ->
|
parse_packet(#mqtt_packet_header{type = ?PUBLISH, qos = QoS}, Bin,
|
||||||
|
#{strict_mode := StrictMode, version := Ver}) ->
|
||||||
{TopicName, Rest} = parse_utf8_string(Bin),
|
{TopicName, Rest} = parse_utf8_string(Bin),
|
||||||
{PacketId, Rest1} = case QoS of
|
{PacketId, Rest1} = case QoS of
|
||||||
?QOS_0 -> {undefined, Rest};
|
?QOS_0 -> {undefined, Rest};
|
||||||
_ -> parse_packet_id(Rest)
|
_ -> parse_packet_id(Rest)
|
||||||
end,
|
end,
|
||||||
(PacketId =/= undefined) andalso validate_packet_id(PacketId),
|
(PacketId =/= undefined) andalso
|
||||||
|
StrictMode andalso validate_packet_id(PacketId),
|
||||||
{Properties, Payload} = parse_properties(Rest1, Ver),
|
{Properties, Payload} = parse_properties(Rest1, Ver),
|
||||||
Publish = #mqtt_packet_publish{topic_name = TopicName,
|
Publish = #mqtt_packet_publish{topic_name = TopicName,
|
||||||
packet_id = PacketId,
|
packet_id = PacketId,
|
||||||
|
@ -215,15 +218,15 @@ parse_packet(#mqtt_packet_header{type = ?PUBLISH, qos = QoS}, Bin, #{version :=
|
||||||
},
|
},
|
||||||
{Publish, Payload};
|
{Publish, Payload};
|
||||||
|
|
||||||
parse_packet(#mqtt_packet_header{type = PubAck}, <<PacketId:16/big>>, _Options)
|
parse_packet(#mqtt_packet_header{type = PubAck}, <<PacketId:16/big>>, #{strict_mode := StrictMode})
|
||||||
when ?PUBACK =< PubAck, PubAck =< ?PUBCOMP ->
|
when ?PUBACK =< PubAck, PubAck =< ?PUBCOMP ->
|
||||||
ok = validate_packet_id(PacketId),
|
StrictMode andalso validate_packet_id(PacketId),
|
||||||
#mqtt_packet_puback{packet_id = PacketId, reason_code = 0};
|
#mqtt_packet_puback{packet_id = PacketId, reason_code = 0};
|
||||||
|
|
||||||
parse_packet(#mqtt_packet_header{type = PubAck}, <<PacketId:16/big, ReasonCode, Rest/binary>>,
|
parse_packet(#mqtt_packet_header{type = PubAck}, <<PacketId:16/big, ReasonCode, Rest/binary>>,
|
||||||
#{version := Ver = ?MQTT_PROTO_V5})
|
#{strict_mode := StrictMode, version := Ver = ?MQTT_PROTO_V5})
|
||||||
when ?PUBACK =< PubAck, PubAck =< ?PUBCOMP ->
|
when ?PUBACK =< PubAck, PubAck =< ?PUBCOMP ->
|
||||||
ok = validate_packet_id(PacketId),
|
StrictMode andalso validate_packet_id(PacketId),
|
||||||
{Properties, <<>>} = parse_properties(Rest, Ver),
|
{Properties, <<>>} = parse_properties(Rest, Ver),
|
||||||
#mqtt_packet_puback{packet_id = PacketId,
|
#mqtt_packet_puback{packet_id = PacketId,
|
||||||
reason_code = ReasonCode,
|
reason_code = ReasonCode,
|
||||||
|
@ -231,8 +234,8 @@ parse_packet(#mqtt_packet_header{type = PubAck}, <<PacketId:16/big, ReasonCode,
|
||||||
};
|
};
|
||||||
|
|
||||||
parse_packet(#mqtt_packet_header{type = ?SUBSCRIBE}, <<PacketId:16/big, Rest/binary>>,
|
parse_packet(#mqtt_packet_header{type = ?SUBSCRIBE}, <<PacketId:16/big, Rest/binary>>,
|
||||||
#{version := Ver}) ->
|
#{strict_mode := StrictMode, version := Ver}) ->
|
||||||
ok = validate_packet_id(PacketId),
|
StrictMode andalso validate_packet_id(PacketId),
|
||||||
{Properties, Rest1} = parse_properties(Rest, Ver),
|
{Properties, Rest1} = parse_properties(Rest, Ver),
|
||||||
TopicFilters = parse_topic_filters(subscribe, Rest1),
|
TopicFilters = parse_topic_filters(subscribe, Rest1),
|
||||||
ok = validate_subqos([QoS || {_, #{qos := QoS}} <- TopicFilters]),
|
ok = validate_subqos([QoS || {_, #{qos := QoS}} <- TopicFilters]),
|
||||||
|
@ -242,8 +245,8 @@ parse_packet(#mqtt_packet_header{type = ?SUBSCRIBE}, <<PacketId:16/big, Rest/bin
|
||||||
};
|
};
|
||||||
|
|
||||||
parse_packet(#mqtt_packet_header{type = ?SUBACK}, <<PacketId:16/big, Rest/binary>>,
|
parse_packet(#mqtt_packet_header{type = ?SUBACK}, <<PacketId:16/big, Rest/binary>>,
|
||||||
#{version := Ver}) ->
|
#{strict_mode := StrictMode, version := Ver}) ->
|
||||||
ok = validate_packet_id(PacketId),
|
StrictMode andalso validate_packet_id(PacketId),
|
||||||
{Properties, Rest1} = parse_properties(Rest, Ver),
|
{Properties, Rest1} = parse_properties(Rest, Ver),
|
||||||
ReasonCodes = parse_reason_codes(Rest1),
|
ReasonCodes = parse_reason_codes(Rest1),
|
||||||
#mqtt_packet_suback{packet_id = PacketId,
|
#mqtt_packet_suback{packet_id = PacketId,
|
||||||
|
@ -252,8 +255,8 @@ parse_packet(#mqtt_packet_header{type = ?SUBACK}, <<PacketId:16/big, Rest/binary
|
||||||
};
|
};
|
||||||
|
|
||||||
parse_packet(#mqtt_packet_header{type = ?UNSUBSCRIBE}, <<PacketId:16/big, Rest/binary>>,
|
parse_packet(#mqtt_packet_header{type = ?UNSUBSCRIBE}, <<PacketId:16/big, Rest/binary>>,
|
||||||
#{version := Ver}) ->
|
#{strict_mode := StrictMode, version := Ver}) ->
|
||||||
ok = validate_packet_id(PacketId),
|
StrictMode andalso validate_packet_id(PacketId),
|
||||||
{Properties, Rest1} = parse_properties(Rest, Ver),
|
{Properties, Rest1} = parse_properties(Rest, Ver),
|
||||||
TopicFilters = parse_topic_filters(unsubscribe, Rest1),
|
TopicFilters = parse_topic_filters(unsubscribe, Rest1),
|
||||||
#mqtt_packet_unsubscribe{packet_id = PacketId,
|
#mqtt_packet_unsubscribe{packet_id = PacketId,
|
||||||
|
@ -261,13 +264,14 @@ parse_packet(#mqtt_packet_header{type = ?UNSUBSCRIBE}, <<PacketId:16/big, Rest/b
|
||||||
topic_filters = TopicFilters
|
topic_filters = TopicFilters
|
||||||
};
|
};
|
||||||
|
|
||||||
parse_packet(#mqtt_packet_header{type = ?UNSUBACK}, <<PacketId:16/big>>, _Options) ->
|
parse_packet(#mqtt_packet_header{type = ?UNSUBACK}, <<PacketId:16/big>>,
|
||||||
ok = validate_packet_id(PacketId),
|
#{strict_mode := StrictMode}) ->
|
||||||
|
StrictMode andalso validate_packet_id(PacketId),
|
||||||
#mqtt_packet_unsuback{packet_id = PacketId};
|
#mqtt_packet_unsuback{packet_id = PacketId};
|
||||||
|
|
||||||
parse_packet(#mqtt_packet_header{type = ?UNSUBACK}, <<PacketId:16/big, Rest/binary>>,
|
parse_packet(#mqtt_packet_header{type = ?UNSUBACK}, <<PacketId:16/big, Rest/binary>>,
|
||||||
#{version := Ver}) ->
|
#{strict_mode := StrictMode, version := Ver}) ->
|
||||||
ok = validate_packet_id(PacketId),
|
StrictMode andalso validate_packet_id(PacketId),
|
||||||
{Properties, Rest1} = parse_properties(Rest, Ver),
|
{Properties, Rest1} = parse_properties(Rest, Ver),
|
||||||
ReasonCodes = parse_reason_codes(Rest1),
|
ReasonCodes = parse_reason_codes(Rest1),
|
||||||
#mqtt_packet_unsuback{packet_id = PacketId,
|
#mqtt_packet_unsuback{packet_id = PacketId,
|
||||||
|
@ -296,8 +300,7 @@ parse_will_message(Packet = #mqtt_packet_connect{will_flag = true,
|
||||||
will_topic = Topic,
|
will_topic = Topic,
|
||||||
will_payload = Payload
|
will_payload = Payload
|
||||||
}, Rest2};
|
}, Rest2};
|
||||||
parse_will_message(Packet, Bin) ->
|
parse_will_message(Packet, Bin) -> {Packet, Bin}.
|
||||||
{Packet, Bin}.
|
|
||||||
|
|
||||||
-compile({inline, [parse_packet_id/1]}).
|
-compile({inline, [parse_packet_id/1]}).
|
||||||
parse_packet_id(<<PacketId:16/big, Rest/binary>>) ->
|
parse_packet_id(<<PacketId:16/big, Rest/binary>>) ->
|
||||||
|
@ -720,6 +723,7 @@ validate_header(?DISCONNECT, 0, 0, 0) -> ok;
|
||||||
validate_header(?AUTH, 0, 0, 0) -> ok;
|
validate_header(?AUTH, 0, 0, 0) -> ok;
|
||||||
validate_header(_Type, _Dup, _QoS, _Rt) -> error(bad_frame_header).
|
validate_header(_Type, _Dup, _QoS, _Rt) -> error(bad_frame_header).
|
||||||
|
|
||||||
|
-compile({inline, [validate_packet_id/1]}).
|
||||||
validate_packet_id(0) -> error(bad_packet_id);
|
validate_packet_id(0) -> error(bad_packet_id);
|
||||||
validate_packet_id(_) -> ok.
|
validate_packet_id(_) -> ok.
|
||||||
|
|
||||||
|
|
|
@ -176,5 +176,5 @@ format(Port) when is_integer(Port) ->
|
||||||
format({Addr, Port}) when is_list(Addr) ->
|
format({Addr, Port}) when is_list(Addr) ->
|
||||||
io_lib:format("~s:~w", [Addr, Port]);
|
io_lib:format("~s:~w", [Addr, Port]);
|
||||||
format({Addr, Port}) when is_tuple(Addr) ->
|
format({Addr, Port}) when is_tuple(Addr) ->
|
||||||
io_lib:format("~s:~w", [esockd_net:ntoab(Addr), Port]).
|
io_lib:format("~s:~w", [inet:ntoa(Addr), Port]).
|
||||||
|
|
||||||
|
|
|
@ -112,5 +112,5 @@ reason({Error, _}) when is_atom(Error) -> Error;
|
||||||
reason(_) -> internal_error.
|
reason(_) -> internal_error.
|
||||||
|
|
||||||
-compile({inline, [ntoa/1]}).
|
-compile({inline, [ntoa/1]}).
|
||||||
ntoa(IpAddr) -> iolist_to_binary(esockd_net:ntoa(IpAddr)).
|
ntoa(IpAddr) -> iolist_to_binary(inet:ntoa(IpAddr)).
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,10 @@
|
||||||
, load_expand_plugin/1
|
, load_expand_plugin/1
|
||||||
]).
|
]).
|
||||||
|
|
||||||
|
-ifdef(TEST).
|
||||||
|
-compile(export_all).
|
||||||
|
-compile(nowarn_export_all).
|
||||||
|
-endif.
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
%% APIs
|
%% APIs
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
@ -82,7 +86,7 @@ load_expand_plugin(PluginDir) ->
|
||||||
init_expand_plugin_config(PluginDir),
|
init_expand_plugin_config(PluginDir),
|
||||||
Ebin = filename:join([PluginDir, "ebin"]),
|
Ebin = filename:join([PluginDir, "ebin"]),
|
||||||
code:add_patha(Ebin),
|
code:add_patha(Ebin),
|
||||||
Modules = filelib:wildcard(filename:join([Ebin ++ "*.beam"])),
|
Modules = filelib:wildcard(filename:join([Ebin, "*.beam"])),
|
||||||
lists:foreach(fun(Mod) ->
|
lists:foreach(fun(Mod) ->
|
||||||
Module = list_to_atom(filename:basename(Mod, ".beam")),
|
Module = list_to_atom(filename:basename(Mod, ".beam")),
|
||||||
code:load_file(Module)
|
code:load_file(Module)
|
||||||
|
|
|
@ -157,7 +157,7 @@
|
||||||
|
|
||||||
-define(STATS_KEYS, [subscriptions_cnt,
|
-define(STATS_KEYS, [subscriptions_cnt,
|
||||||
subscriptions_max,
|
subscriptions_max,
|
||||||
inflight,
|
inflight_cnt,
|
||||||
inflight_max,
|
inflight_max,
|
||||||
mqueue_len,
|
mqueue_len,
|
||||||
mqueue_max,
|
mqueue_max,
|
||||||
|
|
|
@ -43,7 +43,7 @@ start_link() ->
|
||||||
supervisor:start_link({local, ?SUP}, ?MODULE, []).
|
supervisor:start_link({local, ?SUP}, ?MODULE, []).
|
||||||
|
|
||||||
-spec(start_child(supervisor:child_spec()) -> startchild_ret()).
|
-spec(start_child(supervisor:child_spec()) -> startchild_ret()).
|
||||||
start_child(ChildSpec) when is_tuple(ChildSpec) ->
|
start_child(ChildSpec) when is_map(ChildSpec) ->
|
||||||
supervisor:start_child(?SUP, ChildSpec).
|
supervisor:start_child(?SUP, ChildSpec).
|
||||||
|
|
||||||
-spec(start_child(module(), worker | supervisor) -> startchild_ret()).
|
-spec(start_child(module(), worker | supervisor) -> startchild_ret()).
|
||||||
|
|
|
@ -45,6 +45,11 @@
|
||||||
, terminate/2
|
, terminate/2
|
||||||
]).
|
]).
|
||||||
|
|
||||||
|
-ifdef(TEST).
|
||||||
|
-compile(export_all).
|
||||||
|
-compile(nowarn_export_all).
|
||||||
|
-endif.
|
||||||
|
|
||||||
-import(emqx_topic, [systop/1]).
|
-import(emqx_topic, [systop/1]).
|
||||||
-import(emqx_misc, [start_timer/2]).
|
-import(emqx_misc, [start_timer/2]).
|
||||||
|
|
||||||
|
@ -192,7 +197,7 @@ uptime(hours, H) when H < 24 ->
|
||||||
uptime(hours, H) ->
|
uptime(hours, H) ->
|
||||||
[uptime(days, H div 24), integer_to_list(H rem 24), " hours, "];
|
[uptime(days, H div 24), integer_to_list(H rem 24), " hours, "];
|
||||||
uptime(days, D) ->
|
uptime(days, D) ->
|
||||||
[integer_to_list(D), " days,"].
|
[integer_to_list(D), " days, "].
|
||||||
|
|
||||||
publish(uptime, Uptime) ->
|
publish(uptime, Uptime) ->
|
||||||
safe_publish(systop(uptime), Uptime);
|
safe_publish(systop(uptime), Uptime);
|
||||||
|
|
|
@ -177,7 +177,7 @@ suppress(Key, SuccFun, State = #{events := Events}) ->
|
||||||
end.
|
end.
|
||||||
|
|
||||||
procinfo(Pid) ->
|
procinfo(Pid) ->
|
||||||
case {emqx_vm:get_process_info(Pid), emqx_vm:get_process_gc(Pid)} of
|
case {emqx_vm:get_process_info(Pid), emqx_vm:get_process_gc_info(Pid)} of
|
||||||
{undefined, _} -> undefined;
|
{undefined, _} -> undefined;
|
||||||
{_, undefined} -> undefined;
|
{_, undefined} -> undefined;
|
||||||
{Info, GcInfo} -> Info ++ GcInfo
|
{Info, GcInfo} -> Info ++ GcInfo
|
||||||
|
|
216
src/emqx_vm.erl
216
src/emqx_vm.erl
|
@ -19,6 +19,7 @@
|
||||||
-export([ schedulers/0
|
-export([ schedulers/0
|
||||||
, scheduler_usage/1
|
, scheduler_usage/1
|
||||||
, microsecs/0
|
, microsecs/0
|
||||||
|
, system_info_keys/0
|
||||||
, get_system_info/0
|
, get_system_info/0
|
||||||
, get_system_info/1
|
, get_system_info/1
|
||||||
, get_memory/0
|
, get_memory/0
|
||||||
|
@ -26,11 +27,12 @@
|
||||||
, loads/0
|
, loads/0
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-export([ get_process_list/0
|
-export([ process_info_keys/0
|
||||||
, get_process_info/0
|
, get_process_info/0
|
||||||
, get_process_info/1
|
, get_process_info/1
|
||||||
, get_process_gc/0
|
, process_gc_info_keys/0
|
||||||
, get_process_gc/1
|
, get_process_gc_info/0
|
||||||
|
, get_process_gc_info/1
|
||||||
, get_process_group_leader_info/1
|
, get_process_group_leader_info/1
|
||||||
, get_process_limit/0
|
, get_process_limit/0
|
||||||
]).
|
]).
|
||||||
|
@ -62,86 +64,83 @@
|
||||||
sl_alloc,
|
sl_alloc,
|
||||||
ll_alloc,
|
ll_alloc,
|
||||||
fix_alloc,
|
fix_alloc,
|
||||||
std_alloc]).
|
std_alloc
|
||||||
|
]).
|
||||||
|
|
||||||
-define(PROCESS_LIST, [initial_call,
|
-define(PROCESS_INFO_KEYS, [initial_call,
|
||||||
reductions,
|
current_function,
|
||||||
memory,
|
registered_name,
|
||||||
message_queue_len,
|
status,
|
||||||
current_function]).
|
message_queue_len,
|
||||||
|
group_leader,
|
||||||
|
priority,
|
||||||
|
trap_exit,
|
||||||
|
reductions,
|
||||||
|
%%binary,
|
||||||
|
last_calls,
|
||||||
|
catchlevel,
|
||||||
|
trace,
|
||||||
|
suspending,
|
||||||
|
sequential_trace_token,
|
||||||
|
error_handler
|
||||||
|
]).
|
||||||
|
|
||||||
-define(PROCESS_INFO, [initial_call,
|
-define(PROCESS_GC_KEYS, [memory,
|
||||||
current_function,
|
total_heap_size,
|
||||||
registered_name,
|
heap_size,
|
||||||
status,
|
stack_size,
|
||||||
message_queue_len,
|
min_heap_size
|
||||||
group_leader,
|
]).
|
||||||
priority,
|
|
||||||
trap_exit,
|
|
||||||
reductions,
|
|
||||||
%%binary,
|
|
||||||
last_calls,
|
|
||||||
catchlevel,
|
|
||||||
trace,
|
|
||||||
suspending,
|
|
||||||
sequential_trace_token,
|
|
||||||
error_handler]).
|
|
||||||
|
|
||||||
-define(PROCESS_GC, [memory,
|
-define(SYSTEM_INFO_KEYS, [allocated_areas,
|
||||||
total_heap_size,
|
allocator,
|
||||||
heap_size,
|
alloc_util_allocators,
|
||||||
stack_size,
|
build_type,
|
||||||
min_heap_size]).
|
check_io,
|
||||||
%fullsweep_after]).
|
compat_rel,
|
||||||
|
creation,
|
||||||
-define(SYSTEM_INFO, [allocated_areas,
|
debug_compiled,
|
||||||
allocator,
|
dist,
|
||||||
alloc_util_allocators,
|
dist_ctrl,
|
||||||
build_type,
|
driver_version,
|
||||||
check_io,
|
elib_malloc,
|
||||||
compat_rel,
|
dist_buf_busy_limit,
|
||||||
creation,
|
%fullsweep_after, % included in garbage_collection
|
||||||
debug_compiled,
|
garbage_collection,
|
||||||
dist,
|
%global_heaps_size, % deprecated
|
||||||
dist_ctrl,
|
heap_sizes,
|
||||||
driver_version,
|
heap_type,
|
||||||
elib_malloc,
|
info,
|
||||||
dist_buf_busy_limit,
|
kernel_poll,
|
||||||
%fullsweep_after, % included in garbage_collection
|
loaded,
|
||||||
garbage_collection,
|
logical_processors,
|
||||||
%global_heaps_size, % deprecated
|
logical_processors_available,
|
||||||
heap_sizes,
|
logical_processors_online,
|
||||||
heap_type,
|
machine,
|
||||||
info,
|
%min_heap_size, % included in garbage_collection
|
||||||
kernel_poll,
|
%min_bin_vheap_size, % included in garbage_collection
|
||||||
loaded,
|
modified_timing_level,
|
||||||
logical_processors,
|
multi_scheduling,
|
||||||
logical_processors_available,
|
multi_scheduling_blockers,
|
||||||
logical_processors_online,
|
otp_release,
|
||||||
machine,
|
port_count,
|
||||||
%min_heap_size, % included in garbage_collection
|
process_count,
|
||||||
%min_bin_vheap_size, % included in garbage_collection
|
process_limit,
|
||||||
modified_timing_level,
|
scheduler_bind_type,
|
||||||
multi_scheduling,
|
scheduler_bindings,
|
||||||
multi_scheduling_blockers,
|
scheduler_id,
|
||||||
otp_release,
|
schedulers,
|
||||||
port_count,
|
schedulers_online,
|
||||||
process_count,
|
smp_support,
|
||||||
process_limit,
|
system_version,
|
||||||
scheduler_bind_type,
|
system_architecture,
|
||||||
scheduler_bindings,
|
threads,
|
||||||
scheduler_id,
|
thread_pool_size,
|
||||||
schedulers,
|
trace_control_word,
|
||||||
schedulers_online,
|
update_cpu_info,
|
||||||
smp_support,
|
version,
|
||||||
system_version,
|
wordsize
|
||||||
system_architecture,
|
]).
|
||||||
threads,
|
|
||||||
thread_pool_size,
|
|
||||||
trace_control_word,
|
|
||||||
update_cpu_info,
|
|
||||||
version,
|
|
||||||
wordsize]).
|
|
||||||
|
|
||||||
-define(SOCKET_OPTS, [active,
|
-define(SOCKET_OPTS, [active,
|
||||||
broadcast,
|
broadcast,
|
||||||
|
@ -166,7 +165,8 @@
|
||||||
send_timeout,
|
send_timeout,
|
||||||
send_timeout_close,
|
send_timeout_close,
|
||||||
sndbuf,
|
sndbuf,
|
||||||
tos]).
|
tos
|
||||||
|
]).
|
||||||
|
|
||||||
schedulers() ->
|
schedulers() ->
|
||||||
erlang:system_info(schedulers).
|
erlang:system_info(schedulers).
|
||||||
|
@ -178,16 +178,16 @@ microsecs() ->
|
||||||
loads() ->
|
loads() ->
|
||||||
[{load1, ftos(avg1()/256)},
|
[{load1, ftos(avg1()/256)},
|
||||||
{load5, ftos(avg5()/256)},
|
{load5, ftos(avg5()/256)},
|
||||||
{load15, ftos(avg15()/256)}].
|
{load15, ftos(avg15()/256)}
|
||||||
|
].
|
||||||
|
|
||||||
|
system_info_keys() -> ?SYSTEM_INFO_KEYS.
|
||||||
|
|
||||||
get_system_info() ->
|
get_system_info() ->
|
||||||
[{Key, format_system_info(Key, get_system_info(Key))} || Key <- ?SYSTEM_INFO].
|
[{Key, format_system_info(Key, get_system_info(Key))} || Key <- ?SYSTEM_INFO_KEYS].
|
||||||
|
|
||||||
get_system_info(Key) ->
|
get_system_info(Key) ->
|
||||||
try erlang:system_info(Key) catch
|
try erlang:system_info(Key) catch error:badarg-> undefined end.
|
||||||
error:badarg->undefined
|
|
||||||
end.
|
|
||||||
%% conversion functions for erlang:system_info(Key)
|
|
||||||
|
|
||||||
format_system_info(allocated_areas, List) ->
|
format_system_info(allocated_areas, List) ->
|
||||||
[convert_allocated_areas(Value) || Value <- List];
|
[convert_allocated_areas(Value) || Value <- List];
|
||||||
|
@ -221,8 +221,9 @@ convert_allocated_areas({Key, Value}) ->
|
||||||
|
|
||||||
mem_info() ->
|
mem_info() ->
|
||||||
Dataset = memsup:get_system_memory_data(),
|
Dataset = memsup:get_system_memory_data(),
|
||||||
[{total_memory, proplists:get_value(total_memory, Dataset)},
|
Total = proplists:get_value(total_memory, Dataset),
|
||||||
{used_memory, proplists:get_value(total_memory, Dataset) - proplists:get_value(free_memory, Dataset)}].
|
Free = proplists:get_value(free_memory, Dataset),
|
||||||
|
[{total_memory, Total}, {used_memory, Total - Free}].
|
||||||
|
|
||||||
ftos(F) ->
|
ftos(F) ->
|
||||||
S = io_lib:format("~.2f", [F]), S.
|
S = io_lib:format("~.2f", [F]), S.
|
||||||
|
@ -300,24 +301,24 @@ container_value(Props, Pos, Type, Container) ->
|
||||||
TypeProps = proplists:get_value(Type, Props),
|
TypeProps = proplists:get_value(Type, Props),
|
||||||
element(Pos, lists:keyfind(Container, 1, TypeProps)).
|
element(Pos, lists:keyfind(Container, 1, TypeProps)).
|
||||||
|
|
||||||
get_process_list()->
|
process_info_keys() ->
|
||||||
[get_process_list(Pid) || Pid <- processes()].
|
?PROCESS_INFO_KEYS.
|
||||||
|
|
||||||
get_process_list(Pid) when is_pid(Pid) ->
|
|
||||||
[{pid, Pid} | [process_info(Pid, Key) || Key <- ?PROCESS_LIST]].
|
|
||||||
|
|
||||||
get_process_info() ->
|
get_process_info() ->
|
||||||
[get_process_info(Pid) || Pid <- processes()].
|
get_process_info(self()).
|
||||||
get_process_info(Pid) when is_pid(Pid) ->
|
get_process_info(Pid) when is_pid(Pid) ->
|
||||||
process_info(Pid, ?PROCESS_INFO).
|
process_info(Pid, ?PROCESS_INFO_KEYS).
|
||||||
|
|
||||||
get_process_gc() ->
|
process_gc_info_keys() ->
|
||||||
[get_process_gc(Pid) || Pid <- processes()].
|
?PROCESS_GC_KEYS.
|
||||||
get_process_gc(Pid) when is_pid(Pid) ->
|
|
||||||
process_info(Pid, ?PROCESS_GC).
|
get_process_gc_info() ->
|
||||||
|
get_process_gc_info(self()).
|
||||||
|
get_process_gc_info(Pid) when is_pid(Pid) ->
|
||||||
|
process_info(Pid, ?PROCESS_GC_KEYS).
|
||||||
|
|
||||||
get_process_group_leader_info(LeaderPid) when is_pid(LeaderPid) ->
|
get_process_group_leader_info(LeaderPid) when is_pid(LeaderPid) ->
|
||||||
[{Key, Value}|| {Key, Value} <- process_info(LeaderPid), lists:member(Key, ?PROCESS_INFO)].
|
[{Key, Value}|| {Key, Value} <- process_info(LeaderPid), lists:member(Key, ?PROCESS_INFO_KEYS)].
|
||||||
|
|
||||||
get_process_limit() ->
|
get_process_limit() ->
|
||||||
erlang:system_info(process_limit).
|
erlang:system_info(process_limit).
|
||||||
|
@ -446,8 +447,7 @@ ports_type_count(Types) ->
|
||||||
|
|
||||||
mapping(Entries) ->
|
mapping(Entries) ->
|
||||||
mapping(Entries, []).
|
mapping(Entries, []).
|
||||||
mapping([], Acc) ->
|
mapping([], Acc) -> Acc;
|
||||||
Acc;
|
|
||||||
mapping([{owner, V}|Entries], Acc) when is_pid(V) ->
|
mapping([{owner, V}|Entries], Acc) when is_pid(V) ->
|
||||||
OwnerInfo = process_info(V),
|
OwnerInfo = process_info(V),
|
||||||
Owner = proplists:get_value(registered_name, OwnerInfo, undefined),
|
Owner = proplists:get_value(registered_name, OwnerInfo, undefined),
|
||||||
|
@ -470,10 +470,10 @@ cpu_util() ->
|
||||||
compat_windows(Fun) ->
|
compat_windows(Fun) ->
|
||||||
case os:type() of
|
case os:type() of
|
||||||
{win32, nt} -> 0;
|
{win32, nt} -> 0;
|
||||||
_Other -> handle_error(Fun())
|
_Type ->
|
||||||
|
case catch Fun() of
|
||||||
|
Val when is_number(Val) -> Val;
|
||||||
|
_Error -> 0
|
||||||
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
handle_error(Value) when is_number(Value) ->
|
|
||||||
Value;
|
|
||||||
handle_error({error, _Reason}) ->
|
|
||||||
0.
|
|
||||||
|
|
|
@ -218,7 +218,7 @@ websocket_init([Req, Opts]) ->
|
||||||
IdleTimeout = emqx_zone:idle_timeout(Zone),
|
IdleTimeout = emqx_zone:idle_timeout(Zone),
|
||||||
IdleTimer = start_timer(IdleTimeout, idle_timeout),
|
IdleTimer = start_timer(IdleTimeout, idle_timeout),
|
||||||
emqx_misc:tune_heap_size(emqx_zone:oom_policy(Zone)),
|
emqx_misc:tune_heap_size(emqx_zone:oom_policy(Zone)),
|
||||||
emqx_logger:set_metadata_peername(esockd_net:format(Peername)),
|
emqx_logger:set_metadata_peername(esockd:format(Peername)),
|
||||||
{ok, #state{peername = Peername,
|
{ok, #state{peername = Peername,
|
||||||
sockname = Sockname,
|
sockname = Sockname,
|
||||||
sockstate = running,
|
sockstate = running,
|
||||||
|
|
|
@ -32,6 +32,13 @@ init_per_suite(Config) ->
|
||||||
end_per_suite(_Config) ->
|
end_per_suite(_Config) ->
|
||||||
emqx_ct_helpers:stop_apps([]).
|
emqx_ct_helpers:stop_apps([]).
|
||||||
|
|
||||||
|
t_restart(_) ->
|
||||||
|
ConfFile = "test.config",
|
||||||
|
Data = "[{emqx_statsd,[{interval,15000},{push_gateway,\"http://127.0.0.1:9091\"}]}].",
|
||||||
|
file:write_file(ConfFile, list_to_binary(Data)),
|
||||||
|
emqx:restart(ConfFile),
|
||||||
|
file:delete(ConfFile).
|
||||||
|
|
||||||
t_stop_start(_) ->
|
t_stop_start(_) ->
|
||||||
emqx:stop(),
|
emqx:stop(),
|
||||||
false = emqx:is_running(node()),
|
false = emqx:is_running(node()),
|
||||||
|
|
|
@ -19,22 +19,52 @@
|
||||||
-compile(export_all).
|
-compile(export_all).
|
||||||
-compile(nowarn_export_all).
|
-compile(nowarn_export_all).
|
||||||
|
|
||||||
|
-include("emqx_mqtt.hrl").
|
||||||
-include_lib("eunit/include/eunit.hrl").
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
|
|
||||||
all() -> emqx_ct:all(?MODULE).
|
all() -> emqx_ct:all(?MODULE).
|
||||||
|
|
||||||
init_per_testcase(_TestCase, Config) ->
|
init_per_suite(Config) ->
|
||||||
|
emqx_ct_helpers:boot_modules([router, broker]),
|
||||||
|
emqx_ct_helpers:start_apps([]),
|
||||||
Config.
|
Config.
|
||||||
|
|
||||||
end_per_testcase(_TestCase, Config) ->
|
end_per_suite(_Config) ->
|
||||||
Config.
|
emqx_ct_helpers:stop_apps([]).
|
||||||
|
|
||||||
% t_authenticate(_) ->
|
t_authenticate(_) ->
|
||||||
% error('TODO').
|
emqx_zone:set_env(zone, allow_anonymous, false),
|
||||||
|
?assertMatch({error, _}, emqx_access_control:authenticate(clientinfo())),
|
||||||
|
emqx_zone:set_env(zone, allow_anonymous, true),
|
||||||
|
?assertMatch({ok, _}, emqx_access_control:authenticate(clientinfo())).
|
||||||
|
|
||||||
% t_check_acl(_) ->
|
t_check_acl(_) ->
|
||||||
% error('TODO').
|
emqx_zone:set_env(zone, acl_nomatch, deny),
|
||||||
|
application:set_env(emqx, enable_acl_cache, false),
|
||||||
|
Publish = ?PUBLISH_PACKET(?QOS_0, <<"t">>, 1, <<"payload">>),
|
||||||
|
?assertEqual(deny, emqx_access_control:check_acl(clientinfo(), Publish, <<"t">>)),
|
||||||
|
|
||||||
% t_reload_acl(_) ->
|
emqx_zone:set_env(zone, acl_nomatch, allow),
|
||||||
% error('TODO').
|
application:set_env(emqx, enable_acl_cache, true),
|
||||||
|
Publish = ?PUBLISH_PACKET(?QOS_0, <<"t">>, 1, <<"payload">>),
|
||||||
|
?assertEqual(allow, emqx_access_control:check_acl(clientinfo(), Publish, <<"t">>)).
|
||||||
|
|
||||||
|
t_reload_acl(_) ->
|
||||||
|
?assertEqual(ok, emqx_access_control:reload_acl()).
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% 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).
|
||||||
|
|
|
@ -23,15 +23,75 @@
|
||||||
|
|
||||||
all() -> emqx_ct:all(?MODULE).
|
all() -> emqx_ct:all(?MODULE).
|
||||||
|
|
||||||
init_per_testcase(_TestCase, Config) ->
|
init_per_suite(Config) ->
|
||||||
|
emqx_ct_helpers:boot_modules([router, broker]),
|
||||||
|
emqx_ct_helpers:start_apps([]),
|
||||||
Config.
|
Config.
|
||||||
|
|
||||||
end_per_testcase(_TestCase, Config) ->
|
end_per_suite(_Config) ->
|
||||||
Config.
|
emqx_ct_helpers:stop_apps([]).
|
||||||
|
|
||||||
% t_compile(_) ->
|
t_compile(_) ->
|
||||||
% error('TODO').
|
Rule1 = {allow, all, pubsub, <<"%u">>},
|
||||||
|
Compile1 = {allow, all, pubsub, [{pattern,[<<"%u">>]}]},
|
||||||
|
|
||||||
% t_match(_) ->
|
Rule2 = {allow, {ipaddr, "127.0.0.1"}, pubsub, <<"%c">>},
|
||||||
% error('TODO').
|
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"]}))).
|
||||||
|
|
|
@ -38,20 +38,20 @@ end_per_suite(_Config) ->
|
||||||
|
|
||||||
t_add_delete(_) ->
|
t_add_delete(_) ->
|
||||||
Banned = #banned{who = {clientid, <<"TestClient">>},
|
Banned = #banned{who = {clientid, <<"TestClient">>},
|
||||||
reason = <<"test">>,
|
|
||||||
by = <<"banned suite">>,
|
by = <<"banned suite">>,
|
||||||
desc = <<"test">>,
|
reason = <<"test">>,
|
||||||
|
at = erlang:system_time(second),
|
||||||
until = erlang:system_time(second) + 1000
|
until = erlang:system_time(second) + 1000
|
||||||
},
|
},
|
||||||
ok = emqx_banned:add(Banned),
|
ok = emqx_banned:create(Banned),
|
||||||
?assertEqual(1, emqx_banned:info(size)),
|
?assertEqual(1, emqx_banned:info(size)),
|
||||||
ok = emqx_banned:delete({clientid, <<"TestClient">>}),
|
ok = emqx_banned:delete({clientid, <<"TestClient">>}),
|
||||||
?assertEqual(0, emqx_banned:info(size)).
|
?assertEqual(0, emqx_banned:info(size)).
|
||||||
|
|
||||||
t_check(_) ->
|
t_check(_) ->
|
||||||
ok = emqx_banned:add(#banned{who = {clientid, <<"BannedClient">>}}),
|
ok = emqx_banned:create(#banned{who = {clientid, <<"BannedClient">>}}),
|
||||||
ok = emqx_banned:add(#banned{who = {username, <<"BannedUser">>}}),
|
ok = emqx_banned:create(#banned{who = {username, <<"BannedUser">>}}),
|
||||||
ok = emqx_banned:add(#banned{who = {ipaddr, {192,168,0,1}}}),
|
ok = emqx_banned:create(#banned{who = {peerhost, {192,168,0,1}}}),
|
||||||
?assertEqual(3, emqx_banned:info(size)),
|
?assertEqual(3, emqx_banned:info(size)),
|
||||||
ClientInfo1 = #{clientid => <<"BannedClient">>,
|
ClientInfo1 = #{clientid => <<"BannedClient">>,
|
||||||
username => <<"user">>,
|
username => <<"user">>,
|
||||||
|
@ -75,7 +75,7 @@ t_check(_) ->
|
||||||
?assertNot(emqx_banned:check(ClientInfo4)),
|
?assertNot(emqx_banned:check(ClientInfo4)),
|
||||||
ok = emqx_banned:delete({clientid, <<"BannedClient">>}),
|
ok = emqx_banned:delete({clientid, <<"BannedClient">>}),
|
||||||
ok = emqx_banned:delete({username, <<"BannedUser">>}),
|
ok = emqx_banned:delete({username, <<"BannedUser">>}),
|
||||||
ok = emqx_banned:delete({ipaddr, {192,168,0,1}}),
|
ok = emqx_banned:delete({peerhost, {192,168,0,1}}),
|
||||||
?assertNot(emqx_banned:check(ClientInfo1)),
|
?assertNot(emqx_banned:check(ClientInfo1)),
|
||||||
?assertNot(emqx_banned:check(ClientInfo2)),
|
?assertNot(emqx_banned:check(ClientInfo2)),
|
||||||
?assertNot(emqx_banned:check(ClientInfo3)),
|
?assertNot(emqx_banned:check(ClientInfo3)),
|
||||||
|
@ -84,9 +84,8 @@ t_check(_) ->
|
||||||
|
|
||||||
t_unused(_) ->
|
t_unused(_) ->
|
||||||
{ok, Banned} = emqx_banned:start_link(),
|
{ok, Banned} = emqx_banned:start_link(),
|
||||||
ok = emqx_banned:add(#banned{who = {clientid, <<"BannedClient">>},
|
ok = emqx_banned:create(#banned{who = {clientid, <<"BannedClient">>},
|
||||||
until = erlang:system_time(second)
|
until = erlang:system_time(second)}),
|
||||||
}),
|
|
||||||
?assertEqual(ignored, gen_server:call(Banned, unexpected_req)),
|
?assertEqual(ignored, gen_server:call(Banned, unexpected_req)),
|
||||||
?assertEqual(ok, gen_server:cast(Banned, unexpected_msg)),
|
?assertEqual(ok, gen_server:cast(Banned, unexpected_msg)),
|
||||||
?assertEqual(ok, Banned ! ok),
|
?assertEqual(ok, Banned ! ok),
|
||||||
|
|
|
@ -495,9 +495,6 @@ t_enrich_client(_) ->
|
||||||
t_check_banned(_) ->
|
t_check_banned(_) ->
|
||||||
ok = emqx_channel:check_banned(connpkt(), channel()).
|
ok = emqx_channel:check_banned(connpkt(), channel()).
|
||||||
|
|
||||||
t_check_flapping(_) ->
|
|
||||||
ok = emqx_channel:check_flapping(connpkt(), channel()).
|
|
||||||
|
|
||||||
t_auth_connect(_) ->
|
t_auth_connect(_) ->
|
||||||
{ok, _Chan} = emqx_channel:auth_connect(connpkt(), channel()).
|
{ok, _Chan} = emqx_channel:auth_connect(connpkt(), channel()).
|
||||||
|
|
||||||
|
|
|
@ -23,21 +23,23 @@
|
||||||
|
|
||||||
all() -> emqx_ct:all(?MODULE).
|
all() -> emqx_ct:all(?MODULE).
|
||||||
|
|
||||||
init_per_testcase(_TestCase, Config) ->
|
init_per_suite(Config) ->
|
||||||
|
emqx_ct_helpers:boot_modules(all),
|
||||||
|
emqx_ct_helpers:start_apps([]),
|
||||||
Config.
|
Config.
|
||||||
|
|
||||||
end_per_testcase(_TestCase, Config) ->
|
end_per_suite(_Config) ->
|
||||||
Config.
|
emqx_ct_helpers:stop_apps([]).
|
||||||
|
|
||||||
% t_start_link(_) ->
|
t_start_link(_) ->
|
||||||
% error('TODO').
|
emqx_cm_locker:start_link().
|
||||||
|
|
||||||
% t_trans(_) ->
|
t_trans(_) ->
|
||||||
% error('TODO').
|
ok = emqx_cm_locker:trans(undefined, fun(_) -> ok end, []),
|
||||||
|
ok = emqx_cm_locker:trans(<<"clientid">>, fun(_) -> ok end).
|
||||||
% t_lock(_) ->
|
|
||||||
% error('TODO').
|
|
||||||
|
|
||||||
% t_unlock(_) ->
|
|
||||||
% error('TODO').
|
|
||||||
|
|
||||||
|
t_lock_unlocak(_) ->
|
||||||
|
{true, _Nodes} = emqx_cm_locker:lock(<<"clientid">>),
|
||||||
|
{true, _Nodes} = emqx_cm_locker:lock(<<"clientid">>),
|
||||||
|
{true, _Nodes} = emqx_cm_locker:unlock(<<"clientid">>),
|
||||||
|
{true, _Nodes} = emqx_cm_locker:unlock(<<"clientid">>).
|
||||||
|
|
|
@ -45,13 +45,13 @@ t_detect_check(_) ->
|
||||||
peerhost => {127,0,0,1}
|
peerhost => {127,0,0,1}
|
||||||
},
|
},
|
||||||
false = emqx_flapping:detect(ClientInfo),
|
false = emqx_flapping:detect(ClientInfo),
|
||||||
false = emqx_flapping:check(ClientInfo),
|
false = emqx_banned:check(ClientInfo),
|
||||||
false = emqx_flapping:detect(ClientInfo),
|
false = emqx_flapping:detect(ClientInfo),
|
||||||
false = emqx_flapping:check(ClientInfo),
|
false = emqx_banned:check(ClientInfo),
|
||||||
true = emqx_flapping:detect(ClientInfo),
|
true = emqx_flapping:detect(ClientInfo),
|
||||||
timer:sleep(100),
|
timer:sleep(100),
|
||||||
true = emqx_flapping:check(ClientInfo),
|
true = emqx_banned:check(ClientInfo),
|
||||||
timer:sleep(300),
|
timer:sleep(200),
|
||||||
false = emqx_flapping:check(ClientInfo),
|
false = emqx_banned:check(ClientInfo),
|
||||||
ok = emqx_flapping:stop().
|
ok = emqx_flapping:stop().
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,8 @@ all() ->
|
||||||
{group, unsuback},
|
{group, unsuback},
|
||||||
{group, ping},
|
{group, ping},
|
||||||
{group, disconnect},
|
{group, disconnect},
|
||||||
{group, auth}].
|
{group, auth}
|
||||||
|
].
|
||||||
|
|
||||||
groups() ->
|
groups() ->
|
||||||
[{parse, [parallel],
|
[{parse, [parallel],
|
||||||
|
@ -333,7 +334,10 @@ t_serialize_parse_qos1_publish(_) ->
|
||||||
payload = <<"haha">>},
|
payload = <<"haha">>},
|
||||||
?assertEqual(Bin, serialize_to_binary(Packet)),
|
?assertEqual(Bin, serialize_to_binary(Packet)),
|
||||||
?assertMatch(Packet, parse_to_packet(Bin, #{strict_mode => true})),
|
?assertMatch(Packet, parse_to_packet(Bin, #{strict_mode => true})),
|
||||||
?catch_error(bad_packet_id, parse_serialize(?PUBLISH_PACKET(?QOS_1, <<"Topic">>, 0, <<>>))).
|
%% strict_mode = true
|
||||||
|
?catch_error(bad_packet_id, parse_serialize(?PUBLISH_PACKET(?QOS_1, <<"Topic">>, 0, <<>>))),
|
||||||
|
%% strict_mode = false
|
||||||
|
_ = parse_serialize(?PUBLISH_PACKET(?QOS_1, <<"Topic">>, 0, <<>>), #{strict_mode => false}).
|
||||||
|
|
||||||
t_serialize_parse_qos2_publish(_) ->
|
t_serialize_parse_qos2_publish(_) ->
|
||||||
Packet = ?PUBLISH_PACKET(?QOS_2, <<"Topic">>, 1, <<>>),
|
Packet = ?PUBLISH_PACKET(?QOS_2, <<"Topic">>, 1, <<>>),
|
||||||
|
@ -341,7 +345,10 @@ t_serialize_parse_qos2_publish(_) ->
|
||||||
?assertEqual(Packet, parse_serialize(Packet)),
|
?assertEqual(Packet, parse_serialize(Packet)),
|
||||||
?assertEqual(Bin, serialize_to_binary(Packet)),
|
?assertEqual(Bin, serialize_to_binary(Packet)),
|
||||||
?assertMatch(Packet, parse_to_packet(Bin, #{strict_mode => true})),
|
?assertMatch(Packet, parse_to_packet(Bin, #{strict_mode => true})),
|
||||||
?catch_error(bad_packet_id, parse_serialize(?PUBLISH_PACKET(?QOS_2, <<"Topic">>, 0, <<>>))).
|
%% strict_mode = true
|
||||||
|
?catch_error(bad_packet_id, parse_serialize(?PUBLISH_PACKET(?QOS_2, <<"Topic">>, 0, <<>>))),
|
||||||
|
%% strict_mode = false
|
||||||
|
_ = parse_serialize(?PUBLISH_PACKET(?QOS_2, <<"Topic">>, 0, <<>>), #{strict_mode => false}).
|
||||||
|
|
||||||
t_serialize_parse_publish_v5(_) ->
|
t_serialize_parse_publish_v5(_) ->
|
||||||
Props = #{'Payload-Format-Indicator' => 1,
|
Props = #{'Payload-Format-Indicator' => 1,
|
||||||
|
@ -358,7 +365,10 @@ t_serialize_parse_puback(_) ->
|
||||||
Packet = ?PUBACK_PACKET(1),
|
Packet = ?PUBACK_PACKET(1),
|
||||||
?assertEqual(<<64,2,0,1>>, serialize_to_binary(Packet)),
|
?assertEqual(<<64,2,0,1>>, serialize_to_binary(Packet)),
|
||||||
?assertEqual(Packet, parse_serialize(Packet)),
|
?assertEqual(Packet, parse_serialize(Packet)),
|
||||||
?catch_error(bad_packet_id, parse_serialize(?PUBACK_PACKET(0))).
|
%% strict_mode = true
|
||||||
|
?catch_error(bad_packet_id, parse_serialize(?PUBACK_PACKET(0))),
|
||||||
|
%% strict_mode = false
|
||||||
|
?PUBACK_PACKET(0) = parse_serialize(?PUBACK_PACKET(0), #{strict_mode => false}).
|
||||||
|
|
||||||
t_serialize_parse_puback_v3_4(_) ->
|
t_serialize_parse_puback_v3_4(_) ->
|
||||||
Bin = <<64,2,0,1>>,
|
Bin = <<64,2,0,1>>,
|
||||||
|
@ -376,7 +386,10 @@ t_serialize_parse_pubrec(_) ->
|
||||||
Packet = ?PUBREC_PACKET(1),
|
Packet = ?PUBREC_PACKET(1),
|
||||||
?assertEqual(<<5:4,0:4,2,0,1>>, serialize_to_binary(Packet)),
|
?assertEqual(<<5:4,0:4,2,0,1>>, serialize_to_binary(Packet)),
|
||||||
?assertEqual(Packet, parse_serialize(Packet)),
|
?assertEqual(Packet, parse_serialize(Packet)),
|
||||||
?catch_error(bad_packet_id, parse_serialize(?PUBREC_PACKET(0))).
|
%% strict_mode = true
|
||||||
|
?catch_error(bad_packet_id, parse_serialize(?PUBREC_PACKET(0))),
|
||||||
|
%% strict_mode = false
|
||||||
|
?PUBREC_PACKET(0) = parse_serialize(?PUBREC_PACKET(0), #{strict_mode => false}).
|
||||||
|
|
||||||
t_serialize_parse_pubrec_v5(_) ->
|
t_serialize_parse_pubrec_v5(_) ->
|
||||||
Packet = ?PUBREC_PACKET(16, ?RC_SUCCESS, #{'Reason-String' => <<"success">>}),
|
Packet = ?PUBREC_PACKET(16, ?RC_SUCCESS, #{'Reason-String' => <<"success">>}),
|
||||||
|
@ -391,6 +404,9 @@ t_serialize_parse_pubrel(_) ->
|
||||||
Bin0 = <<6:4,0:4,2,0,1>>,
|
Bin0 = <<6:4,0:4,2,0,1>>,
|
||||||
?assertMatch(Packet, parse_to_packet(Bin0, #{strict_mode => false})),
|
?assertMatch(Packet, parse_to_packet(Bin0, #{strict_mode => false})),
|
||||||
?catch_error(bad_frame_header, parse_to_packet(Bin0, #{strict_mode => true})),
|
?catch_error(bad_frame_header, parse_to_packet(Bin0, #{strict_mode => true})),
|
||||||
|
%% strict_mode = false
|
||||||
|
?PUBREL_PACKET(0) = parse_serialize(?PUBREL_PACKET(0), #{strict_mode => false}),
|
||||||
|
%% strict_mode = true
|
||||||
?catch_error(bad_packet_id, parse_serialize(?PUBREL_PACKET(0))).
|
?catch_error(bad_packet_id, parse_serialize(?PUBREL_PACKET(0))).
|
||||||
|
|
||||||
t_serialize_parse_pubrel_v5(_) ->
|
t_serialize_parse_pubrel_v5(_) ->
|
||||||
|
@ -402,6 +418,9 @@ t_serialize_parse_pubcomp(_) ->
|
||||||
Bin = serialize_to_binary(Packet),
|
Bin = serialize_to_binary(Packet),
|
||||||
?assertEqual(<<7:4,0:4,2,0,1>>, Bin),
|
?assertEqual(<<7:4,0:4,2,0,1>>, Bin),
|
||||||
?assertEqual(Packet, parse_serialize(Packet)),
|
?assertEqual(Packet, parse_serialize(Packet)),
|
||||||
|
%% strict_mode = false
|
||||||
|
?PUBCOMP_PACKET(0) = parse_serialize(?PUBCOMP_PACKET(0), #{strict_mode => false}),
|
||||||
|
%% strict_mode = true
|
||||||
?catch_error(bad_packet_id, parse_serialize(?PUBCOMP_PACKET(0))).
|
?catch_error(bad_packet_id, parse_serialize(?PUBCOMP_PACKET(0))).
|
||||||
|
|
||||||
t_serialize_parse_pubcomp_v5(_) ->
|
t_serialize_parse_pubcomp_v5(_) ->
|
||||||
|
@ -419,7 +438,12 @@ t_serialize_parse_subscribe(_) ->
|
||||||
%% SUBSCRIBE with bad qos 0
|
%% SUBSCRIBE with bad qos 0
|
||||||
Bin0 = <<?SUBSCRIBE:4,0:4,11,0,2,0,6,84,111,112,105,99,65,2>>,
|
Bin0 = <<?SUBSCRIBE:4,0:4,11,0,2,0,6,84,111,112,105,99,65,2>>,
|
||||||
?assertMatch(Packet, parse_to_packet(Bin0, #{strict_mode => false})),
|
?assertMatch(Packet, parse_to_packet(Bin0, #{strict_mode => false})),
|
||||||
|
%% strict_mode = false
|
||||||
|
_ = parse_to_packet(Bin0, #{strict_mode => false}),
|
||||||
?catch_error(bad_frame_header, parse_to_packet(Bin0, #{strict_mode => true})),
|
?catch_error(bad_frame_header, parse_to_packet(Bin0, #{strict_mode => true})),
|
||||||
|
%% strict_mode = false
|
||||||
|
_ = parse_serialize(?SUBSCRIBE_PACKET(0, TopicFilters), #{strict_mode => false}),
|
||||||
|
%% strict_mode = true
|
||||||
?catch_error(bad_packet_id, parse_serialize(?SUBSCRIBE_PACKET(0, TopicFilters))),
|
?catch_error(bad_packet_id, parse_serialize(?SUBSCRIBE_PACKET(0, TopicFilters))),
|
||||||
?catch_error(bad_subqos, parse_serialize(?SUBSCRIBE_PACKET(1, [{<<"t">>, #{qos => 3}}]))).
|
?catch_error(bad_subqos, parse_serialize(?SUBSCRIBE_PACKET(1, [{<<"t">>, #{qos => 3}}]))).
|
||||||
|
|
||||||
|
@ -432,6 +456,9 @@ t_serialize_parse_subscribe_v5(_) ->
|
||||||
t_serialize_parse_suback(_) ->
|
t_serialize_parse_suback(_) ->
|
||||||
Packet = ?SUBACK_PACKET(10, [?QOS_0, ?QOS_1, 128]),
|
Packet = ?SUBACK_PACKET(10, [?QOS_0, ?QOS_1, 128]),
|
||||||
?assertEqual(Packet, parse_serialize(Packet)),
|
?assertEqual(Packet, parse_serialize(Packet)),
|
||||||
|
%% strict_mode = false
|
||||||
|
_ = parse_serialize(?SUBACK_PACKET(0, [?QOS_0]), #{strict_mode => false}),
|
||||||
|
%% strict_mode = true
|
||||||
?catch_error(bad_packet_id, parse_serialize(?SUBACK_PACKET(0, [?QOS_0]))).
|
?catch_error(bad_packet_id, parse_serialize(?SUBACK_PACKET(0, [?QOS_0]))).
|
||||||
|
|
||||||
t_serialize_parse_suback_v5(_) ->
|
t_serialize_parse_suback_v5(_) ->
|
||||||
|
@ -451,6 +478,9 @@ t_serialize_parse_unsubscribe(_) ->
|
||||||
Bin0 = <<?UNSUBSCRIBE:4,0:4,10,0,2,0,6,84,111,112,105,99,65>>,
|
Bin0 = <<?UNSUBSCRIBE:4,0:4,10,0,2,0,6,84,111,112,105,99,65>>,
|
||||||
?assertMatch(Packet, parse_to_packet(Bin0, #{strict_mode => false})),
|
?assertMatch(Packet, parse_to_packet(Bin0, #{strict_mode => false})),
|
||||||
?catch_error(bad_frame_header, parse_to_packet(Bin0, #{strict_mode => true})),
|
?catch_error(bad_frame_header, parse_to_packet(Bin0, #{strict_mode => true})),
|
||||||
|
%% strict_mode = false
|
||||||
|
_ = parse_serialize(?UNSUBSCRIBE_PACKET(0, [<<"TopicA">>]), #{strict_mode => false}),
|
||||||
|
%% strict_mode = true
|
||||||
?catch_error(bad_packet_id, parse_serialize(?UNSUBSCRIBE_PACKET(0, [<<"TopicA">>]))).
|
?catch_error(bad_packet_id, parse_serialize(?UNSUBSCRIBE_PACKET(0, [<<"TopicA">>]))).
|
||||||
|
|
||||||
t_serialize_parse_unsubscribe_v5(_) ->
|
t_serialize_parse_unsubscribe_v5(_) ->
|
||||||
|
|
|
@ -138,3 +138,12 @@ drain(Acc) ->
|
||||||
lists:reverse(Acc)
|
lists:reverse(Acc)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
t_rand_seed(_) ->
|
||||||
|
?assert(is_tuple(emqx_misc:rand_seed())).
|
||||||
|
|
||||||
|
t_now_to_secs(_) ->
|
||||||
|
?assert(is_integer(emqx_misc:now_to_secs(os:timestamp()))).
|
||||||
|
|
||||||
|
t_now_to_ms(_) ->
|
||||||
|
?assert(is_integer(emqx_misc:now_to_ms(os:timestamp()))).
|
||||||
|
|
||||||
|
|
|
@ -19,28 +19,53 @@
|
||||||
-compile(export_all).
|
-compile(export_all).
|
||||||
-compile(nowarn_export_all).
|
-compile(nowarn_export_all).
|
||||||
|
|
||||||
|
-include("emqx_mqtt.hrl").
|
||||||
-include_lib("eunit/include/eunit.hrl").
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
|
|
||||||
all() -> emqx_ct:all(?MODULE).
|
all() -> emqx_ct:all(?MODULE).
|
||||||
|
|
||||||
init_per_testcase(_TestCase, Config) ->
|
init_per_suite(Config) ->
|
||||||
|
emqx_ct_helpers:boot_modules(all),
|
||||||
|
emqx_ct_helpers:start_apps([emqx]),
|
||||||
Config.
|
Config.
|
||||||
|
|
||||||
end_per_testcase(_TestCase, Config) ->
|
end_per_suite(_Config) ->
|
||||||
Config.
|
emqx_ct_helpers:stop_apps([emqx]).
|
||||||
|
|
||||||
% t_load(_) ->
|
t_load_unload(_) ->
|
||||||
% error('TODO').
|
?assertEqual({error,already_exists}, emqx_mod_acl_internal:load([])),
|
||||||
|
?assertEqual(ok, emqx_mod_acl_internal:unload([])),
|
||||||
|
?assertEqual(ok, emqx_mod_acl_internal:load([])).
|
||||||
|
|
||||||
% t_unload(_) ->
|
t_all_rules(_) ->
|
||||||
% error('TODO').
|
application:set_env(emqx, acl_file, ""),
|
||||||
|
?assertMatch(#{}, emqx_mod_acl_internal:all_rules()),
|
||||||
|
|
||||||
% t_all_rules(_) ->
|
application:set_env(emqx, acl_file, emqx_ct_helpers:deps_path(emqx, "etc/acl.conf")),
|
||||||
% error('TODO').
|
?assertMatch(#{publish := _, subscribe := _}, emqx_mod_acl_internal:all_rules()).
|
||||||
|
|
||||||
% t_check_acl(_) ->
|
t_check_acl(_) ->
|
||||||
% error('TODO').
|
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(_) ->
|
t_reload_acl(_) ->
|
||||||
% error('TODO').
|
?assertEqual(ok, emqx_mod_acl_internal:reload_acl()).
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% 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).
|
||||||
|
|
|
@ -19,22 +19,44 @@
|
||||||
-compile(export_all).
|
-compile(export_all).
|
||||||
-compile(nowarn_export_all).
|
-compile(nowarn_export_all).
|
||||||
|
|
||||||
|
-include("emqx_mqtt.hrl").
|
||||||
-include_lib("eunit/include/eunit.hrl").
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
|
|
||||||
all() -> emqx_ct:all(?MODULE).
|
all() -> emqx_ct:all(?MODULE).
|
||||||
|
|
||||||
init_per_testcase(_TestCase, Config) ->
|
init_per_suite(Config) ->
|
||||||
|
emqx_ct_helpers:boot_modules(all),
|
||||||
|
emqx_ct_helpers:start_apps([emqx]),
|
||||||
Config.
|
Config.
|
||||||
|
|
||||||
end_per_testcase(_TestCase, Config) ->
|
end_per_suite(_Config) ->
|
||||||
Config.
|
emqx_ct_helpers:stop_apps([emqx]).
|
||||||
|
|
||||||
% t_load(_) ->
|
t_load(_) ->
|
||||||
% error('TODO').
|
?assertEqual(ok, emqx_mod_subscription:load([{<<"connected/%c/%u">>, ?QOS_0}])).
|
||||||
|
|
||||||
% t_on_client_connected(_) ->
|
t_on_client_connected(_) ->
|
||||||
% error('TODO').
|
{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).
|
||||||
|
|
||||||
% t_unload(_) ->
|
t_unload(_) ->
|
||||||
% error('TODO').
|
?assertEqual(ok, emqx_mod_subscription:unload([{<<"connected/%c/%u">>, ?QOS_0}])).
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Internal functions
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
receive_publish(Timeout) ->
|
||||||
|
receive
|
||||||
|
{publish, Publish} -> {ok, Publish}
|
||||||
|
after
|
||||||
|
Timeout -> {error, timeout}
|
||||||
|
end.
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
-compile(export_all).
|
-compile(export_all).
|
||||||
-compile(nowarn_export_all).
|
-compile(nowarn_export_all).
|
||||||
|
|
||||||
|
-include("emqx.hrl").
|
||||||
-include_lib("eunit/include/eunit.hrl").
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
|
|
||||||
all() -> emqx_ct:all(?MODULE).
|
all() -> emqx_ct:all(?MODULE).
|
||||||
|
@ -30,7 +31,7 @@ init_per_suite(Config) ->
|
||||||
|
|
||||||
DataPath = proplists:get_value(data_dir, Config),
|
DataPath = proplists:get_value(data_dir, Config),
|
||||||
AppPath = filename:join([DataPath, "emqx_mini_plugin"]),
|
AppPath = filename:join([DataPath, "emqx_mini_plugin"]),
|
||||||
Cmd = lists:flatten(io_lib:format("cd ~s && make", [AppPath])),
|
Cmd = lists:flatten(io_lib:format("cd ~s && make && cp -r etc _build/default/lib/emqx_mini_plugin/", [AppPath])),
|
||||||
|
|
||||||
ct:pal("Executing ~s~n", [Cmd]),
|
ct:pal("Executing ~s~n", [Cmd]),
|
||||||
ct:pal("~n ~s~n", [os:cmd(Cmd)]),
|
ct:pal("~n ~s~n", [os:cmd(Cmd)]),
|
||||||
|
@ -43,21 +44,6 @@ init_per_suite(Config) ->
|
||||||
|
|
||||||
Config.
|
Config.
|
||||||
|
|
||||||
% t_load_expand_plugin(_) ->
|
|
||||||
% error('TODO').
|
|
||||||
|
|
||||||
% t_list(_) ->
|
|
||||||
% error('TODO').
|
|
||||||
|
|
||||||
% t_find_plugin(_) ->
|
|
||||||
% error('TODO').
|
|
||||||
|
|
||||||
% t_unload(_) ->
|
|
||||||
% error('TODO').
|
|
||||||
|
|
||||||
% t_init(_) ->
|
|
||||||
% error('TODO').
|
|
||||||
|
|
||||||
set_sepecial_cfg(_) ->
|
set_sepecial_cfg(_) ->
|
||||||
ExpandPath = filename:dirname(code:lib_dir(emqx_mini_plugin)),
|
ExpandPath = filename:dirname(code:lib_dir(emqx_mini_plugin)),
|
||||||
|
|
||||||
|
@ -69,8 +55,92 @@ end_per_suite(_Config) ->
|
||||||
emqx_ct_helpers:stop_apps([]).
|
emqx_ct_helpers:stop_apps([]).
|
||||||
|
|
||||||
t_load(_) ->
|
t_load(_) ->
|
||||||
{error, load_app_fail} = emqx_plugins:load_expand_plugin("./not_existed_path/"),
|
?assertEqual([], emqx_plugins:load()),
|
||||||
|
?assertEqual([], emqx_plugins:unload()),
|
||||||
|
|
||||||
|
?assertEqual({error, not_found}, emqx_plugins:load(not_existed_plugin)),
|
||||||
|
?assertMatch({ok, _}, emqx_plugins:load(emqx_mini_plugin)),
|
||||||
|
?assertEqual({error, already_started}, emqx_plugins:load(emqx_mini_plugin)),
|
||||||
|
?assertEqual(ok, emqx_plugins:unload(emqx_mini_plugin)),
|
||||||
|
?assertEqual({error, not_started}, emqx_plugins:unload(emqx_mini_plugin)),
|
||||||
|
|
||||||
|
application:set_env(emqx, expand_plugins_dir, undefined),
|
||||||
|
application:set_env(emqx, plugins_loaded_file, undefined),
|
||||||
|
?assertEqual(ignore, emqx_plugins:load()),
|
||||||
|
?assertEqual(ignore, emqx_plugins:unload()).
|
||||||
|
|
||||||
|
|
||||||
|
t_init_config(_) ->
|
||||||
|
ConfFile = "emqx_mini_plugin.config",
|
||||||
|
Data = "[{emqx_mini_plugin,[{mininame ,test}]}].",
|
||||||
|
file:write_file(ConfFile, list_to_binary(Data)),
|
||||||
|
?assertEqual(ok, emqx_plugins:init_config(ConfFile)),
|
||||||
|
file:delete(ConfFile),
|
||||||
|
?assertEqual({ok,test}, application:get_env(emqx_mini_plugin, mininame)).
|
||||||
|
|
||||||
|
t_load_expand_plugin(_) ->
|
||||||
|
?assertEqual({error, load_app_fail}, emqx_plugins:load_expand_plugin("./not_existed_path/")).
|
||||||
|
|
||||||
|
t_list(_) ->
|
||||||
|
?assertMatch([{plugin, _, _, _, _, _, _, _, _} | _ ], emqx_plugins:list()).
|
||||||
|
|
||||||
|
t_find_plugin(_) ->
|
||||||
|
?assertMatch({plugin, emqx_mini_plugin, _, _, _, _, _, _, _}, emqx_plugins:find_plugin(emqx_mini_plugin)).
|
||||||
|
|
||||||
|
t_plugin_type(_) ->
|
||||||
|
?assertEqual(auth, emqx_plugins:plugin_type(auth)),
|
||||||
|
?assertEqual(protocol, emqx_plugins:plugin_type(protocol)),
|
||||||
|
?assertEqual(backend, emqx_plugins:plugin_type(backend)),
|
||||||
|
?assertEqual(bridge, emqx_plugins:plugin_type(bridge)),
|
||||||
|
?assertEqual(feature, emqx_plugins:plugin_type(undefined)).
|
||||||
|
|
||||||
|
t_with_loaded_file(_) ->
|
||||||
|
?assertMatch({error, _}, emqx_plugins:with_loaded_file("./not_existed_path/", fun(_) -> ok end)).
|
||||||
|
|
||||||
|
t_plugin_loaded(_) ->
|
||||||
|
?assertEqual(ok, emqx_plugins:plugin_loaded(emqx_mini_plugin, false)),
|
||||||
|
?assertEqual(ok, emqx_plugins:plugin_loaded(emqx_mini_plugin, true)).
|
||||||
|
|
||||||
|
t_plugin_unloaded(_) ->
|
||||||
|
?assertEqual(ok, emqx_plugins:plugin_unloaded(emqx_mini_plugin, false)),
|
||||||
|
?assertEqual(ok, emqx_plugins:plugin_unloaded(emqx_mini_plugin, true)).
|
||||||
|
|
||||||
|
t_plugin(_) ->
|
||||||
|
try
|
||||||
|
emqx_plugins:plugin(not_existed_plugin, undefined)
|
||||||
|
catch
|
||||||
|
_Error:Reason:_Stacktrace ->
|
||||||
|
?assertEqual({plugin_not_found,not_existed_plugin}, Reason)
|
||||||
|
end,
|
||||||
|
?assertMatch({plugin, emqx_mini_plugin, _, _, _, _, _, _, _}, emqx_plugins:plugin(emqx_mini_plugin, undefined)).
|
||||||
|
|
||||||
|
t_filter_plugins(_) ->
|
||||||
|
?assertEqual([name1, name2], emqx_plugins:filter_plugins([name1, {name2,true}, {name3, false}])).
|
||||||
|
|
||||||
|
t_load_plugin(_) ->
|
||||||
|
ok = meck:new(application, [unstick, non_strict, passthrough, no_history]),
|
||||||
|
ok = meck:expect(application, load, fun(already_loaded_app) -> {error, {already_loaded, already_loaded_app}};
|
||||||
|
(error_app) -> {error, error};
|
||||||
|
(_) -> ok end),
|
||||||
|
ok = meck:expect(application, ensure_all_started, fun(already_loaded_app) -> {error, {already_loaded_app, already_loaded}};
|
||||||
|
(error_app) -> {error, error};
|
||||||
|
(App) -> {ok, App} end),
|
||||||
|
|
||||||
|
?assertMatch({error, _}, emqx_plugins:load_plugin(#plugin{name = already_loaded_app}, true)),
|
||||||
|
?assertMatch({ok, _}, emqx_plugins:load_plugin(#plugin{name = normal}, true)),
|
||||||
|
?assertMatch({error,_}, emqx_plugins:load_plugin(#plugin{name = error_app}, true)),
|
||||||
|
|
||||||
|
ok = meck:unload(application).
|
||||||
|
|
||||||
|
t_unload_plugin(_) ->
|
||||||
|
ok = meck:new(application, [unstick, non_strict, passthrough, no_history]),
|
||||||
|
ok = meck:expect(application, stop, fun(not_started_app) -> {error, {not_started, not_started_app}};
|
||||||
|
(error_app) -> {error, error};
|
||||||
|
(_) -> ok end),
|
||||||
|
|
||||||
|
?assertEqual(ok, emqx_plugins:unload_plugin(not_started_app, true)),
|
||||||
|
?assertEqual(ok, emqx_plugins:unload_plugin(normal, true)),
|
||||||
|
?assertEqual({error,error}, emqx_plugins:unload_plugin(error_app, true)),
|
||||||
|
|
||||||
|
ok = meck:unload(application).
|
||||||
|
|
||||||
{error, not_started} = emqx_plugins:unload(emqx_mini_plugin),
|
|
||||||
{ok, _} = emqx_plugins:load(emqx_mini_plugin),
|
|
||||||
ok = emqx_plugins:unload(emqx_mini_plugin).
|
|
||||||
|
|
|
@ -29,6 +29,9 @@
|
||||||
emqx_ct_helpers:wait_for(
|
emqx_ct_helpers:wait_for(
|
||||||
?FUNCTION_NAME, ?LINE, fun() -> For end, Timeout)).
|
?FUNCTION_NAME, ?LINE, fun() -> For end, Timeout)).
|
||||||
|
|
||||||
|
-define(ack, shared_sub_ack).
|
||||||
|
-define(no_ack, no_ack).
|
||||||
|
|
||||||
all() -> emqx_ct:all(?SUITE).
|
all() -> emqx_ct:all(?SUITE).
|
||||||
|
|
||||||
init_per_suite(Config) ->
|
init_per_suite(Config) ->
|
||||||
|
@ -39,17 +42,22 @@ init_per_suite(Config) ->
|
||||||
end_per_suite(_Config) ->
|
end_per_suite(_Config) ->
|
||||||
emqx_ct_helpers:stop_apps([]).
|
emqx_ct_helpers:stop_apps([]).
|
||||||
|
|
||||||
% t_is_ack_required(_) ->
|
t_is_ack_required(_) ->
|
||||||
% error('TODO').
|
?assertEqual(false, emqx_shared_sub:is_ack_required(#message{headers = #{}})).
|
||||||
|
|
||||||
% t_maybe_nack_dropped(_) ->
|
t_maybe_nack_dropped(_) ->
|
||||||
% error('TODO').
|
?assertEqual(ok, emqx_shared_sub:maybe_nack_dropped(#message{headers = #{}})),
|
||||||
|
?assertEqual(ok, emqx_shared_sub:maybe_nack_dropped(#message{headers = #{shared_dispatch_ack => {self(), for_test}}})),
|
||||||
|
?assertEqual(ok,receive {for_test, {shared_sub_nack, dropped}} -> ok after 100 -> timeout end).
|
||||||
|
|
||||||
% t_nack_no_connection(_) ->
|
t_nack_no_connection(_) ->
|
||||||
% error('TODO').
|
?assertEqual(ok, emqx_shared_sub:nack_no_connection(#message{headers = #{shared_dispatch_ack => {self(), for_test}}})),
|
||||||
|
?assertEqual(ok,receive {for_test, {shared_sub_nack, no_connection}} -> ok after 100 -> timeout end).
|
||||||
|
|
||||||
% t_maybe_ack(_) ->
|
t_maybe_ack(_) ->
|
||||||
% error('TODO').
|
?assertEqual(#message{headers = #{}}, emqx_shared_sub:maybe_ack(#message{headers = #{}})),
|
||||||
|
?assertEqual(#message{headers = #{shared_dispatch_ack => ?no_ack}}, emqx_shared_sub:maybe_ack(#message{headers = #{shared_dispatch_ack => {self(), for_test}}})),
|
||||||
|
?assertEqual(ok,receive {for_test, ?ack} -> ok after 100 -> timeout end).
|
||||||
|
|
||||||
% t_subscribers(_) ->
|
% t_subscribers(_) ->
|
||||||
% error('TODO').
|
% error('TODO').
|
||||||
|
@ -239,14 +247,23 @@ last_message(ExpectedPayload, Pids) ->
|
||||||
<<"not yet?">>
|
<<"not yet?">>
|
||||||
end.
|
end.
|
||||||
|
|
||||||
% t_dispatch(_) ->
|
t_dispatch(_) ->
|
||||||
% error('TODO').
|
ok = ensure_config(random),
|
||||||
|
Topic = <<"foo">>,
|
||||||
|
?assertEqual({error, no_subscribers}, emqx_shared_sub:dispatch(<<"group1">>, Topic, #delivery{message = #message{}})),
|
||||||
|
emqx:subscribe(Topic, #{qos => 2, share => <<"group1">>}),
|
||||||
|
?assertEqual(ok, emqx_shared_sub:dispatch(<<"group1">>, Topic, #delivery{message = #message{}})).
|
||||||
|
|
||||||
% t_unsubscribe(_) ->
|
% t_unsubscribe(_) ->
|
||||||
% error('TODO').
|
% error('TODO').
|
||||||
|
|
||||||
% t_subscribe(_) ->
|
% t_subscribe(_) ->
|
||||||
% error('TODO').
|
% error('TODO').
|
||||||
|
t_uncovered_func(_) ->
|
||||||
|
ignored = gen_server:call(emqx_shared_sub, ignored),
|
||||||
|
ok = gen_server:cast(emqx_shared_sub, ignored),
|
||||||
|
ignored = emqx_shared_sub ! ignored,
|
||||||
|
{mnesia_table_event, []} = emqx_shared_sub ! {mnesia_table_event, []}.
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
%% help functions
|
%% help functions
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
|
||||||
|
%%
|
||||||
|
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
%% you may not use this file except in compliance with the License.
|
||||||
|
%% 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_sup_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_child(_) ->
|
||||||
|
?assertMatch({error, _}, emqx_sup:start_child(undef, worker)),
|
||||||
|
?assertMatch({error, not_found}, emqx_sup:stop_child(undef)),
|
||||||
|
?assertMatch({error, _}, emqx_sup:start_child(emqx_broker_sup, supervisor)),
|
||||||
|
?assertEqual(ok, emqx_sup:stop_child(emqx_broker_sup)),
|
||||||
|
?assertMatch({ok, _}, emqx_sup:start_child(emqx_broker_sup, supervisor)).
|
|
@ -49,8 +49,11 @@ end_per_suite(_Config) ->
|
||||||
% t_sysdescr(_) ->
|
% t_sysdescr(_) ->
|
||||||
% error('TODO').
|
% error('TODO').
|
||||||
|
|
||||||
% t_uptime(_) ->
|
t_uptime(_) ->
|
||||||
% error('TODO').
|
?assertEqual(<<"1 seconds">>, iolist_to_binary(emqx_sys:uptime(seconds, 1))),
|
||||||
|
?assertEqual(<<"1 minutes, 0 seconds">>, iolist_to_binary(emqx_sys:uptime(seconds, 60))),
|
||||||
|
?assertEqual(<<"1 hours, 0 minutes, 0 seconds">>, iolist_to_binary(emqx_sys:uptime(seconds, 3600))),
|
||||||
|
?assertEqual(<<"1 days, 0 hours, 0 minutes, 0 seconds">>, iolist_to_binary(emqx_sys:uptime(seconds, 86400))).
|
||||||
|
|
||||||
% t_datetime(_) ->
|
% t_datetime(_) ->
|
||||||
% error('TODO').
|
% error('TODO').
|
||||||
|
|
|
@ -69,11 +69,25 @@ init_per_testcase(t_sys_mon2, Config) ->
|
||||||
ok;
|
ok;
|
||||||
(_) -> ok
|
(_) -> ok
|
||||||
end),
|
end),
|
||||||
|
Config;
|
||||||
|
init_per_testcase(_, Config) ->
|
||||||
|
emqx_ct_helpers:boot_modules(all),
|
||||||
|
emqx_ct_helpers:start_apps([]),
|
||||||
Config.
|
Config.
|
||||||
|
|
||||||
end_per_testcase(_, _Config) ->
|
end_per_testcase(_, _Config) ->
|
||||||
emqx_ct_helpers:stop_apps([]).
|
emqx_ct_helpers:stop_apps([]).
|
||||||
|
|
||||||
|
t_procinfo(_) ->
|
||||||
|
ok = meck:new(emqx_vm, [passthrough, no_history]),
|
||||||
|
ok = meck:expect(emqx_vm, get_process_info, fun(_) -> [] end),
|
||||||
|
ok = meck:expect(emqx_vm, get_process_gc_info, fun(_) -> [] end),
|
||||||
|
?assertEqual([], emqx_sys_mon:procinfo([])),
|
||||||
|
ok = meck:expect(emqx_vm, get_process_info, fun(_) -> ok end),
|
||||||
|
ok = meck:expect(emqx_vm, get_process_gc_info, fun(_) -> undefined end),
|
||||||
|
?assertEqual(undefined, emqx_sys_mon:procinfo([])),
|
||||||
|
ok = meck:unload(emqx_vm).
|
||||||
|
|
||||||
t_sys_mon(_Config) ->
|
t_sys_mon(_Config) ->
|
||||||
lists:foreach(
|
lists:foreach(
|
||||||
fun({PidOrPort, SysMonName,ValidateInfo, InfoOrPort}) ->
|
fun({PidOrPort, SysMonName,ValidateInfo, InfoOrPort}) ->
|
||||||
|
|
|
@ -21,115 +21,29 @@
|
||||||
|
|
||||||
-include_lib("eunit/include/eunit.hrl").
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
|
|
||||||
-define(SYSTEM_INFO, [allocated_areas,
|
|
||||||
allocator,
|
|
||||||
alloc_util_allocators,
|
|
||||||
build_type,
|
|
||||||
check_io,
|
|
||||||
compat_rel,
|
|
||||||
creation,
|
|
||||||
debug_compiled,
|
|
||||||
dist,
|
|
||||||
dist_ctrl,
|
|
||||||
driver_version,
|
|
||||||
elib_malloc,
|
|
||||||
dist_buf_busy_limit,
|
|
||||||
%fullsweep_after, % included in garbage_collection
|
|
||||||
garbage_collection,
|
|
||||||
%global_heaps_size, % deprecated
|
|
||||||
heap_sizes,
|
|
||||||
heap_type,
|
|
||||||
info,
|
|
||||||
kernel_poll,
|
|
||||||
loaded,
|
|
||||||
logical_processors,
|
|
||||||
logical_processors_available,
|
|
||||||
logical_processors_online,
|
|
||||||
machine,
|
|
||||||
%min_heap_size, % included in garbage_collection
|
|
||||||
%min_bin_vheap_size, % included in garbage_collection
|
|
||||||
modified_timing_level,
|
|
||||||
multi_scheduling,
|
|
||||||
multi_scheduling_blockers,
|
|
||||||
otp_release,
|
|
||||||
port_count,
|
|
||||||
process_count,
|
|
||||||
process_limit,
|
|
||||||
scheduler_bind_type,
|
|
||||||
scheduler_bindings,
|
|
||||||
scheduler_id,
|
|
||||||
schedulers,
|
|
||||||
schedulers_online,
|
|
||||||
smp_support,
|
|
||||||
system_version,
|
|
||||||
system_architecture,
|
|
||||||
threads,
|
|
||||||
thread_pool_size,
|
|
||||||
trace_control_word,
|
|
||||||
update_cpu_info,
|
|
||||||
version,
|
|
||||||
wordsize]).
|
|
||||||
|
|
||||||
-define(PROCESS_INFO, [initial_call,
|
|
||||||
current_function,
|
|
||||||
registered_name,
|
|
||||||
status,
|
|
||||||
message_queue_len,
|
|
||||||
group_leader,
|
|
||||||
priority,
|
|
||||||
trap_exit,
|
|
||||||
reductions,
|
|
||||||
%%binary,
|
|
||||||
last_calls,
|
|
||||||
catchlevel,
|
|
||||||
trace,
|
|
||||||
suspending,
|
|
||||||
sequential_trace_token,
|
|
||||||
error_handler]).
|
|
||||||
|
|
||||||
-define(PROCESS_GC, [memory,
|
|
||||||
total_heap_size,
|
|
||||||
heap_size,
|
|
||||||
stack_size,
|
|
||||||
min_heap_size]).
|
|
||||||
%fullsweep_after]).
|
|
||||||
|
|
||||||
all() -> emqx_ct:all(?MODULE).
|
all() -> emqx_ct:all(?MODULE).
|
||||||
|
|
||||||
t_load(_Config) ->
|
t_load(_Config) ->
|
||||||
?assertMatch([{load1, _},
|
?assertMatch([{load1, _}, {load5, _}, {load15, _}], emqx_vm:loads()).
|
||||||
{load5, _},
|
|
||||||
{load15, _}
|
|
||||||
], emqx_vm:loads()).
|
|
||||||
|
|
||||||
t_systeminfo(_Config) ->
|
t_systeminfo(_Config) ->
|
||||||
Keys = [Key || {Key, _} <- emqx_vm:get_system_info()],
|
?assertEqual(emqx_vm:system_info_keys(),
|
||||||
?SYSTEM_INFO = Keys,
|
[Key || {Key, _} <- emqx_vm:get_system_info()]),
|
||||||
?assertEqual(undefined, emqx_vm:get_system_info(undefined)).
|
?assertEqual(undefined, emqx_vm:get_system_info(undefined)).
|
||||||
|
|
||||||
t_mem_info(_Config) ->
|
t_mem_info(_Config) ->
|
||||||
application:ensure_all_started(os_mon),
|
application:ensure_all_started(os_mon),
|
||||||
MemInfo = emqx_vm:mem_info(),
|
MemInfo = emqx_vm:mem_info(),
|
||||||
[{total_memory, _},
|
[{total_memory, _}, {used_memory, _}]= MemInfo,
|
||||||
{used_memory, _}]= MemInfo,
|
|
||||||
application:stop(os_mon).
|
application:stop(os_mon).
|
||||||
|
|
||||||
t_process_list(_Config) ->
|
|
||||||
Pid = self(),
|
|
||||||
ProcessInfo = emqx_vm:get_process_list(),
|
|
||||||
true = lists:member({pid, Pid}, lists:concat(ProcessInfo)).
|
|
||||||
|
|
||||||
t_process_info(_Config) ->
|
t_process_info(_Config) ->
|
||||||
ProcessInfos = emqx_vm:get_process_info(),
|
ProcessInfo = emqx_vm:get_process_info(),
|
||||||
ProcessInfo = lists:last(ProcessInfos),
|
?assertEqual(emqx_vm:process_info_keys(), [K || {K, _V}<- ProcessInfo]).
|
||||||
Keys = [K || {K, _V}<- ProcessInfo],
|
|
||||||
?PROCESS_INFO = Keys.
|
|
||||||
|
|
||||||
t_process_gc(_Config) ->
|
t_process_gc(_Config) ->
|
||||||
ProcessGcs = emqx_vm:get_process_gc(),
|
GcInfo = emqx_vm:get_process_gc_info(),
|
||||||
ProcessGc = lists:last(ProcessGcs),
|
?assertEqual(emqx_vm:process_gc_info_keys(), [K || {K, _V}<- GcInfo]).
|
||||||
Keys = [K || {K, _V}<- ProcessGc],
|
|
||||||
?PROCESS_GC = Keys.
|
|
||||||
|
|
||||||
t_get_ets_list(_Config) ->
|
t_get_ets_list(_Config) ->
|
||||||
ets:new(test, [named_table]),
|
ets:new(test, [named_table]),
|
||||||
|
|
Loading…
Reference in New Issue