summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--deps/rabbitmq_mqtt/src/Elixir.RabbitMQ.CLI.Ctl.Commands.DecommissionMqttNodeCommand.erl45
-rw-r--r--deps/rabbitmq_mqtt/src/Elixir.RabbitMQ.CLI.Ctl.Commands.ListMqttConnectionsCommand.erl62
-rw-r--r--deps/rabbitmq_mqtt/src/mqtt_machine.erl284
-rw-r--r--deps/rabbitmq_mqtt/src/mqtt_machine_v0.erl112
-rw-r--r--deps/rabbitmq_mqtt/src/mqtt_node.erl119
-rw-r--r--deps/rabbitmq_mqtt/src/rabbit_mqtt.erl62
-rw-r--r--deps/rabbitmq_mqtt/src/rabbit_mqtt_collector.erl35
-rw-r--r--deps/rabbitmq_mqtt/src/rabbit_mqtt_confirms.erl71
-rw-r--r--deps/rabbitmq_mqtt/src/rabbit_mqtt_ff.erl20
-rw-r--r--deps/rabbitmq_mqtt/src/rabbit_mqtt_internal_event_handler.erl4
-rw-r--r--deps/rabbitmq_mqtt/src/rabbit_mqtt_keepalive.erl80
-rw-r--r--deps/rabbitmq_mqtt/src/rabbit_mqtt_packet.erl300
-rw-r--r--deps/rabbitmq_mqtt/src/rabbit_mqtt_processor.erl2427
-rw-r--r--deps/rabbitmq_mqtt/src/rabbit_mqtt_qos0_queue.erl228
-rw-r--r--deps/rabbitmq_mqtt/src/rabbit_mqtt_reader.erl543
-rw-r--r--deps/rabbitmq_mqtt/src/rabbit_mqtt_retained_msg_store_dets.erl52
-rw-r--r--deps/rabbitmq_mqtt/src/rabbit_mqtt_retained_msg_store_ets.erl52
-rw-r--r--deps/rabbitmq_mqtt/src/rabbit_mqtt_retained_msg_store_noop.erl12
-rw-r--r--deps/rabbitmq_mqtt/src/rabbit_mqtt_retainer.erl64
-rw-r--r--deps/rabbitmq_mqtt/src/rabbit_mqtt_retainer_sup.erl41
-rw-r--r--deps/rabbitmq_mqtt/src/rabbit_mqtt_sup.erl139
-rw-r--r--deps/rabbitmq_mqtt/src/rabbit_mqtt_util.erl98
-rw-r--r--deps/rabbitmq_mqtt/test/auth_SUITE.erl937
-rw-r--r--deps/rabbitmq_mqtt/test/cluster_SUITE.erl101
-rw-r--r--deps/rabbitmq_mqtt/test/command_SUITE.erl72
-rw-r--r--deps/rabbitmq_mqtt/test/config_SUITE.erl77
-rw-r--r--deps/rabbitmq_mqtt/test/config_schema_SUITE.erl29
-rw-r--r--deps/rabbitmq_mqtt/test/event_recorder.erl9
-rw-r--r--deps/rabbitmq_mqtt/test/ff_SUITE.erl128
-rw-r--r--deps/rabbitmq_mqtt/test/java_SUITE.erl80
-rw-r--r--deps/rabbitmq_mqtt/test/mqtt_machine_SUITE.erl90
-rw-r--r--deps/rabbitmq_mqtt/test/processor_SUITE.erl106
-rw-r--r--deps/rabbitmq_mqtt/test/proxy_protocol_SUITE.erl63
-rw-r--r--deps/rabbitmq_mqtt/test/rabbit_auth_backend_mqtt_mock.erl24
-rw-r--r--deps/rabbitmq_mqtt/test/reader_SUITE.erl186
-rw-r--r--deps/rabbitmq_mqtt/test/retainer_SUITE.erl84
-rw-r--r--deps/rabbitmq_mqtt/test/shared_SUITE.erl1245
-rw-r--r--deps/rabbitmq_mqtt/test/util.erl160
-rw-r--r--deps/rabbitmq_mqtt/test/util_SUITE.erl17
-rw-r--r--deps/rabbitmq_web_mqtt/src/rabbit_web_mqtt_app.erl125
-rw-r--r--deps/rabbitmq_web_mqtt/src/rabbit_web_mqtt_handler.erl347
-rw-r--r--deps/rabbitmq_web_mqtt/src/rabbit_web_mqtt_stream_handler.erl1
-rw-r--r--deps/rabbitmq_web_mqtt/test/config_schema_SUITE.erl29
-rw-r--r--deps/rabbitmq_web_mqtt/test/proxy_protocol_SUITE.erl78
-rw-r--r--deps/rabbitmq_web_mqtt/test/system_SUITE.erl39
-rw-r--r--rebar.config7
46 files changed, 5384 insertions, 3500 deletions
diff --git a/deps/rabbitmq_mqtt/src/Elixir.RabbitMQ.CLI.Ctl.Commands.DecommissionMqttNodeCommand.erl b/deps/rabbitmq_mqtt/src/Elixir.RabbitMQ.CLI.Ctl.Commands.DecommissionMqttNodeCommand.erl
index fa8e09341c..a89dad8d16 100644
--- a/deps/rabbitmq_mqtt/src/Elixir.RabbitMQ.CLI.Ctl.Commands.DecommissionMqttNodeCommand.erl
+++ b/deps/rabbitmq_mqtt/src/Elixir.RabbitMQ.CLI.Ctl.Commands.DecommissionMqttNodeCommand.erl
@@ -10,18 +10,20 @@
-behaviour('Elixir.RabbitMQ.CLI.CommandBehaviour').
--export([scopes/0,
- switches/0,
- aliases/0,
- usage/0,
- usage_doc_guides/0,
- banner/2,
- validate/2,
- merge_defaults/2,
- run/2,
- output/2,
- description/0,
- help_section/0]).
+-export([
+ scopes/0,
+ switches/0,
+ aliases/0,
+ usage/0,
+ usage_doc_guides/0,
+ banner/2,
+ validate/2,
+ merge_defaults/2,
+ run/2,
+ output/2,
+ description/0,
+ help_section/0
+]).
scopes() -> [ctl].
switches() -> [].
@@ -48,20 +50,29 @@ usage() ->
usage_doc_guides() ->
[?MQTT_GUIDE_URL].
-run([Node], #{node := NodeName,
- timeout := Timeout}) ->
+run([Node], #{
+ node := NodeName,
+ timeout := Timeout
+}) ->
case rabbit_misc:rpc_call(NodeName, rabbit_mqtt_collector, leave, [Node], Timeout) of
{badrpc, _} = Error ->
Error;
nodedown ->
- {ok, list_to_binary(io_lib:format("Node ~ts is down but has been successfully removed"
- " from the cluster", [Node]))};
+ {ok,
+ list_to_binary(
+ io_lib:format(
+ "Node ~ts is down but has been successfully removed"
+ " from the cluster",
+ [Node]
+ )
+ )};
Result ->
%% 'ok' or 'timeout'
Result
end.
-banner([Node], _) -> list_to_binary(io_lib:format("Removing node ~ts from the list of MQTT nodes...", [Node])).
+banner([Node], _) ->
+ list_to_binary(io_lib:format("Removing node ~ts from the list of MQTT nodes...", [Node])).
output(Result, _Opts) ->
'Elixir.RabbitMQ.CLI.DefaultOutput':output(Result).
diff --git a/deps/rabbitmq_mqtt/src/Elixir.RabbitMQ.CLI.Ctl.Commands.ListMqttConnectionsCommand.erl b/deps/rabbitmq_mqtt/src/Elixir.RabbitMQ.CLI.Ctl.Commands.ListMqttConnectionsCommand.erl
index 07265b56e1..f40868cd83 100644
--- a/deps/rabbitmq_mqtt/src/Elixir.RabbitMQ.CLI.Ctl.Commands.ListMqttConnectionsCommand.erl
+++ b/deps/rabbitmq_mqtt/src/Elixir.RabbitMQ.CLI.Ctl.Commands.ListMqttConnectionsCommand.erl
@@ -10,20 +10,22 @@
-behaviour('Elixir.RabbitMQ.CLI.CommandBehaviour').
--export([formatter/0,
- scopes/0,
- switches/0,
- aliases/0,
- usage/0,
- usage_additional/0,
- usage_doc_guides/0,
- banner/2,
- validate/2,
- merge_defaults/2,
- run/2,
- output/2,
- description/0,
- help_section/0]).
+-export([
+ formatter/0,
+ scopes/0,
+ switches/0,
+ aliases/0,
+ usage/0,
+ usage_additional/0,
+ usage_doc_guides/0,
+ banner/2,
+ validate/2,
+ merge_defaults/2,
+ run/2,
+ output/2,
+ description/0,
+ help_section/0
+]).
formatter() -> 'Elixir.RabbitMQ.CLI.Formatters.Table'.
scopes() -> [ctl, diagnostics].
@@ -37,10 +39,14 @@ help_section() ->
validate(Args, _) ->
InfoItems = lists:map(fun atom_to_list/1, ?INFO_ITEMS),
- case 'Elixir.RabbitMQ.CLI.Ctl.InfoKeys':validate_info_keys(Args,
- InfoItems) of
+ case
+ 'Elixir.RabbitMQ.CLI.Ctl.InfoKeys':validate_info_keys(
+ Args,
+ InfoItems
+ )
+ of
{ok, _} -> ok;
- Error -> Error
+ Error -> Error
end.
merge_defaults([], Opts) ->
@@ -55,19 +61,22 @@ usage_additional() ->
Prefix = <<" must be one of ">>,
InfoItems = 'Elixir.Enum':join(lists:usort(?INFO_ITEMS), <<", ">>),
[
- {<<"<column>">>, <<Prefix/binary, InfoItems/binary>>}
+ {<<"<column>">>, <<Prefix/binary, InfoItems/binary>>}
].
usage_doc_guides() ->
[?MQTT_GUIDE_URL].
-run(Args, #{node := NodeName,
- timeout := Timeout,
- verbose := Verbose}) ->
- InfoKeys = case Verbose of
- true -> ?INFO_ITEMS;
- false -> 'Elixir.RabbitMQ.CLI.Ctl.InfoKeys':prepare_info_keys(Args)
- end,
+run(Args, #{
+ node := NodeName,
+ timeout := Timeout,
+ verbose := Verbose
+}) ->
+ InfoKeys =
+ case Verbose of
+ true -> ?INFO_ITEMS;
+ false -> 'Elixir.RabbitMQ.CLI.Ctl.InfoKeys':prepare_info_keys(Args)
+ end,
Nodes = 'Elixir.RabbitMQ.CLI.Core.Helpers':nodes_in_cluster(NodeName),
@@ -78,7 +87,8 @@ run(Args, #{node := NodeName,
[Nodes, InfoKeys],
Timeout,
InfoKeys,
- length(Nodes)).
+ length(Nodes)
+ ).
banner(_, _) -> <<"Listing MQTT connections ...">>.
diff --git a/deps/rabbitmq_mqtt/src/mqtt_machine.erl b/deps/rabbitmq_mqtt/src/mqtt_machine.erl
index 20e69a7a2a..36c0c7e70f 100644
--- a/deps/rabbitmq_mqtt/src/mqtt_machine.erl
+++ b/deps/rabbitmq_mqtt/src/mqtt_machine.erl
@@ -9,13 +9,15 @@
-include("mqtt_machine.hrl").
--export([version/0,
- which_module/1,
- init/1,
- apply/3,
- state_enter/2,
- notify_connection/2,
- overview/1]).
+-export([
+ version/0,
+ which_module/1,
+ init/1,
+ apply/3,
+ state_enter/2,
+ notify_connection/2,
+ overview/1
+]).
-type state() :: #machine_state{}.
@@ -24,9 +26,10 @@
-type reply() :: {ok, term()} | {error, term()}.
-type client_id() :: term().
--type command() :: {register, client_id(), pid()} |
- {unregister, client_id(), pid()} |
- list.
+-type command() ::
+ {register, client_id(), pid()}
+ | {unregister, client_id(), pid()}
+ | list.
version() -> 1.
which_module(1) -> ?MODULE;
@@ -38,93 +41,130 @@ init(_Conf) ->
-spec apply(map(), command(), state()) ->
{state(), reply(), ra_machine:effects()}.
-apply(_Meta, {register, ClientId, Pid},
- #machine_state{client_ids = Ids,
- pids = Pids0} = State0) ->
+apply(
+ _Meta,
+ {register, ClientId, Pid},
+ #machine_state{
+ client_ids = Ids,
+ pids = Pids0
+ } = State0
+) ->
{Effects, Ids1, Pids} =
case maps:find(ClientId, Ids) of
{ok, OldPid} when Pid =/= OldPid ->
- Effects0 = [{demonitor, process, OldPid},
- {monitor, process, Pid},
- {mod_call, ?MODULE, notify_connection,
- [OldPid, duplicate_id]}],
- Pids2 = case maps:take(OldPid, Pids0) of
- error ->
- Pids0;
- {[ClientId], Pids1} ->
- Pids1;
- {ClientIds, Pids1} ->
- Pids1#{ClientId => lists:delete(ClientId, ClientIds)}
- end,
- Pids3 = maps:update_with(Pid, fun(CIds) -> [ClientId | CIds] end,
- [ClientId], Pids2),
+ Effects0 = [
+ {demonitor, process, OldPid},
+ {monitor, process, Pid},
+ {mod_call, ?MODULE, notify_connection, [OldPid, duplicate_id]}
+ ],
+ Pids2 =
+ case maps:take(OldPid, Pids0) of
+ error ->
+ Pids0;
+ {[ClientId], Pids1} ->
+ Pids1;
+ {ClientIds, Pids1} ->
+ Pids1#{ClientId => lists:delete(ClientId, ClientIds)}
+ end,
+ Pids3 = maps:update_with(
+ Pid,
+ fun(CIds) -> [ClientId | CIds] end,
+ [ClientId],
+ Pids2
+ ),
{Effects0, maps:remove(ClientId, Ids), Pids3};
-
- {ok, Pid} ->
+ {ok, Pid} ->
{[], Ids, Pids0};
error ->
- Pids1 = maps:update_with(Pid, fun(CIds) -> [ClientId | CIds] end,
- [ClientId], Pids0),
+ Pids1 = maps:update_with(
+ Pid,
+ fun(CIds) -> [ClientId | CIds] end,
+ [ClientId],
+ Pids0
+ ),
Effects0 = [{monitor, process, Pid}],
{Effects0, Ids, Pids1}
end,
- State = State0#machine_state{client_ids = maps:put(ClientId, Pid, Ids1),
- pids = Pids},
+ State = State0#machine_state{
+ client_ids = maps:put(ClientId, Pid, Ids1),
+ pids = Pids
+ },
{State, ok, Effects};
+apply(
+ Meta,
+ {unregister, ClientId, Pid},
+ #machine_state{
+ client_ids = Ids,
+ pids = Pids0
+ } = State0
+) ->
+ State =
+ case maps:find(ClientId, Ids) of
+ {ok, Pid} ->
+ Pids =
+ case maps:get(Pid, Pids0, undefined) of
+ undefined ->
+ Pids0;
+ [ClientId] ->
+ maps:remove(Pid, Pids0);
+ Cids ->
+ Pids0#{Pid => lists:delete(ClientId, Cids)}
+ end,
-apply(Meta, {unregister, ClientId, Pid}, #machine_state{client_ids = Ids,
- pids = Pids0} = State0) ->
- State = case maps:find(ClientId, Ids) of
- {ok, Pid} ->
- Pids = case maps:get(Pid, Pids0, undefined) of
- undefined ->
- Pids0;
- [ClientId] ->
- maps:remove(Pid, Pids0);
- Cids ->
- Pids0#{Pid => lists:delete(ClientId, Cids)}
- end,
-
- State0#machine_state{client_ids = maps:remove(ClientId, Ids),
- pids = Pids};
- %% don't delete client id that might belong to a newer connection
- %% that kicked the one with Pid out
- {ok, _AnotherPid} ->
- State0;
- error ->
- State0
- end,
+ State0#machine_state{
+ client_ids = maps:remove(ClientId, Ids),
+ pids = Pids
+ };
+ %% don't delete client id that might belong to a newer connection
+ %% that kicked the one with Pid out
+ {ok, _AnotherPid} ->
+ State0;
+ error ->
+ State0
+ end,
Effects0 = [{demonitor, process, Pid}],
%% snapshot only when the map has changed
- Effects = case State of
- State0 -> Effects0;
- _ -> Effects0 ++ snapshot_effects(Meta, State)
- end,
+ Effects =
+ case State of
+ State0 -> Effects0;
+ _ -> Effects0 ++ snapshot_effects(Meta, State)
+ end,
{State, ok, Effects};
-
apply(_Meta, {down, DownPid, noconnection}, State) ->
%% Monitor the node the pid is on (see {nodeup, Node} below)
%% so that we can detect when the node is re-connected and discover the
%% actual fate of the connection processes on it
Effect = {monitor, node, node(DownPid)},
{State, ok, Effect};
-
-apply(Meta, {down, DownPid, _}, #machine_state{client_ids = Ids,
- pids = Pids0} = State0) ->
+apply(
+ Meta,
+ {down, DownPid, _},
+ #machine_state{
+ client_ids = Ids,
+ pids = Pids0
+ } = State0
+) ->
case maps:get(DownPid, Pids0, undefined) of
undefined ->
{State0, ok, []};
ClientIds ->
Ids1 = maps:without(ClientIds, Ids),
- State = State0#machine_state{client_ids = Ids1,
- pids = maps:remove(DownPid, Pids0)},
- Effects = lists:map(fun(Id) ->
- [{mod_call, rabbit_log, debug,
- ["MQTT connection with client id '~ts' failed", [Id]]}]
- end, ClientIds),
+ State = State0#machine_state{
+ client_ids = Ids1,
+ pids = maps:remove(DownPid, Pids0)
+ },
+ Effects = lists:map(
+ fun(Id) ->
+ [
+ {mod_call, rabbit_log, debug, [
+ "MQTT connection with client id '~ts' failed", [Id]
+ ]}
+ ]
+ end,
+ ClientIds
+ ),
{State, ok, Effects ++ snapshot_effects(Meta, State)}
end;
-
apply(_Meta, {nodeup, Node}, State) ->
%% Work out if any pids that were disconnected are still
%% alive.
@@ -133,41 +173,69 @@ apply(_Meta, {nodeup, Node}, State) ->
{State, ok, Effects};
apply(_Meta, {nodedown, _Node}, State) ->
{State, ok};
-
-apply(Meta, {leave, Node}, #machine_state{client_ids = Ids,
- pids = Pids0} = State0) ->
+apply(
+ Meta,
+ {leave, Node},
+ #machine_state{
+ client_ids = Ids,
+ pids = Pids0
+ } = State0
+) ->
{Keep, Remove} = maps:fold(
- fun (ClientId, Pid, {In, Out}) ->
- case node(Pid) =/= Node of
- true ->
- {In#{ClientId => Pid}, Out};
- false ->
- {In, Out#{ClientId => Pid}}
- end
- end, {#{}, #{}}, Ids),
- Effects = maps:fold(fun (ClientId, _Pid, Acc) ->
- Pid = maps:get(ClientId, Ids),
- [
- {demonitor, process, Pid},
- {mod_call, ?MODULE, notify_connection, [Pid, decommission_node]},
- {mod_call, rabbit_log, debug,
- ["MQTT will remove client ID '~ts' from known "
- "as its node has been decommissioned", [ClientId]]}
- ] ++ Acc
- end, [], Remove),
-
- State = State0#machine_state{client_ids = Keep,
- pids = maps:without(maps:keys(Remove), Pids0)},
+ fun(ClientId, Pid, {In, Out}) ->
+ case node(Pid) =/= Node of
+ true ->
+ {In#{ClientId => Pid}, Out};
+ false ->
+ {In, Out#{ClientId => Pid}}
+ end
+ end,
+ {#{}, #{}},
+ Ids
+ ),
+ Effects = maps:fold(
+ fun(ClientId, _Pid, Acc) ->
+ Pid = maps:get(ClientId, Ids),
+ [
+ {demonitor, process, Pid},
+ {mod_call, ?MODULE, notify_connection, [Pid, decommission_node]},
+ {mod_call, rabbit_log, debug, [
+ "MQTT will remove client ID '~ts' from known "
+ "as its node has been decommissioned",
+ [ClientId]
+ ]}
+ ] ++ Acc
+ end,
+ [],
+ Remove
+ ),
+
+ State = State0#machine_state{
+ client_ids = Keep,
+ pids = maps:without(maps:keys(Remove), Pids0)
+ },
{State, ok, Effects ++ snapshot_effects(Meta, State)};
apply(_Meta, {machine_version, 0, 1}, {machine_state, Ids}) ->
Pids = maps:fold(
- fun(Id, Pid, Acc) ->
- maps:update_with(Pid,
- fun(CIds) -> [Id | CIds] end,
- [Id], Acc)
- end, #{}, Ids),
- {#machine_state{client_ids = Ids,
- pids = Pids}, ok, []};
+ fun(Id, Pid, Acc) ->
+ maps:update_with(
+ Pid,
+ fun(CIds) -> [Id | CIds] end,
+ [Id],
+ Acc
+ )
+ end,
+ #{},
+ Ids
+ ),
+ {
+ #machine_state{
+ client_ids = Ids,
+ pids = Pids
+ },
+ ok,
+ []
+ };
apply(_Meta, Unknown, State) ->
logger:error("MQTT Raft state machine v1 received unknown command ~tp", [Unknown]),
{State, {error, {unknown_command, Unknown}}, []}.
@@ -182,17 +250,21 @@ state_enter(_, _) ->
[].
-spec overview(state()) -> map().
-overview(#machine_state{client_ids = ClientIds,
- pids = Pids}) ->
- #{num_client_ids => maps:size(ClientIds),
- num_pids => maps:size(Pids)}.
+overview(#machine_state{
+ client_ids = ClientIds,
+ pids = Pids
+}) ->
+ #{
+ num_client_ids => maps:size(ClientIds),
+ num_pids => maps:size(Pids)
+ }.
%% ==========================
%% Avoids blocking the Raft leader.
-spec notify_connection(pid(), duplicate_id | decommission_node) -> pid().
notify_connection(Pid, Reason) ->
- spawn(fun() -> gen_server2:cast(Pid, Reason) end).
+ spawn(fun() -> gen_server2:cast(Pid, Reason) end).
-spec snapshot_effects(map(), state()) -> ra_machine:effects().
snapshot_effects(#{index := RaftIdx}, State) ->
diff --git a/deps/rabbitmq_mqtt/src/mqtt_machine_v0.erl b/deps/rabbitmq_mqtt/src/mqtt_machine_v0.erl
index 4b32ac88dd..702053b7b5 100644
--- a/deps/rabbitmq_mqtt/src/mqtt_machine_v0.erl
+++ b/deps/rabbitmq_mqtt/src/mqtt_machine_v0.erl
@@ -9,10 +9,12 @@
-include("mqtt_machine_v0.hrl").
--export([init/1,
- apply/3,
- state_enter/2,
- notify_connection/2]).
+-export([
+ init/1,
+ apply/3,
+ state_enter/2,
+ notify_connection/2
+]).
-type state() :: #machine_state{}.
@@ -21,9 +23,10 @@
-type reply() :: {ok, term()} | {error, term()}.
-type client_id() :: term().
--type command() :: {register, client_id(), pid()} |
- {unregister, client_id(), pid()} |
- list.
+-type command() ::
+ {register, client_id(), pid()}
+ | {unregister, client_id(), pid()}
+ | list.
-spec init(config()) -> state().
init(_Conf) ->
@@ -35,53 +38,60 @@ apply(_Meta, {register, ClientId, Pid}, #machine_state{client_ids = Ids} = State
{Effects, Ids1} =
case maps:find(ClientId, Ids) of
{ok, OldPid} when Pid =/= OldPid ->
- Effects0 = [{demonitor, process, OldPid},
- {monitor, process, Pid},
- {mod_call, ?MODULE, notify_connection, [OldPid, duplicate_id]}],
+ Effects0 = [
+ {demonitor, process, OldPid},
+ {monitor, process, Pid},
+ {mod_call, ?MODULE, notify_connection, [OldPid, duplicate_id]}
+ ],
{Effects0, maps:remove(ClientId, Ids)};
_ ->
- Effects0 = [{monitor, process, Pid}],
- {Effects0, Ids}
+ Effects0 = [{monitor, process, Pid}],
+ {Effects0, Ids}
end,
State = State0#machine_state{client_ids = maps:put(ClientId, Pid, Ids1)},
{State, ok, Effects};
-
apply(Meta, {unregister, ClientId, Pid}, #machine_state{client_ids = Ids} = State0) ->
- State = case maps:find(ClientId, Ids) of
- {ok, Pid} -> State0#machine_state{client_ids = maps:remove(ClientId, Ids)};
- %% don't delete client id that might belong to a newer connection
- %% that kicked the one with Pid out
- {ok, _AnotherPid} -> State0;
- error -> State0
- end,
+ State =
+ case maps:find(ClientId, Ids) of
+ {ok, Pid} -> State0#machine_state{client_ids = maps:remove(ClientId, Ids)};
+ %% don't delete client id that might belong to a newer connection
+ %% that kicked the one with Pid out
+ {ok, _AnotherPid} -> State0;
+ error -> State0
+ end,
Effects0 = [{demonitor, process, Pid}],
%% snapshot only when the map has changed
- Effects = case State of
- State0 -> Effects0;
- _ -> Effects0 ++ snapshot_effects(Meta, State)
- end,
+ Effects =
+ case State of
+ State0 -> Effects0;
+ _ -> Effects0 ++ snapshot_effects(Meta, State)
+ end,
{State, ok, Effects};
-
apply(_Meta, {down, DownPid, noconnection}, State) ->
%% Monitor the node the pid is on (see {nodeup, Node} below)
%% so that we can detect when the node is re-connected and discover the
%% actual fate of the connection processes on it
Effect = {monitor, node, node(DownPid)},
{State, ok, Effect};
-
apply(Meta, {down, DownPid, _}, #machine_state{client_ids = Ids} = State0) ->
- Ids1 = maps:filter(fun (_ClientId, Pid) when Pid =:= DownPid ->
- false;
- (_, _) ->
- true
- end, Ids),
+ Ids1 = maps:filter(
+ fun
+ (_ClientId, Pid) when Pid =:= DownPid ->
+ false;
+ (_, _) ->
+ true
+ end,
+ Ids
+ ),
State = State0#machine_state{client_ids = Ids1},
Delta = maps:keys(Ids) -- maps:keys(Ids1),
- Effects = lists:map(fun(Id) ->
- [{mod_call, rabbit_log, debug,
- ["MQTT connection with client id '~ts' failed", [Id]]}] end, Delta),
+ Effects = lists:map(
+ fun(Id) ->
+ [{mod_call, rabbit_log, debug, ["MQTT connection with client id '~ts' failed", [Id]]}]
+ end,
+ Delta
+ ),
{State, ok, Effects ++ snapshot_effects(Meta, State)};
-
apply(_Meta, {nodeup, Node}, State) ->
%% Work out if any pids that were disconnected are still
%% alive.
@@ -90,25 +100,29 @@ apply(_Meta, {nodeup, Node}, State) ->
{State, ok, Effects};
apply(_Meta, {nodedown, _Node}, State) ->
{State, ok};
-
apply(Meta, {leave, Node}, #machine_state{client_ids = Ids} = State0) ->
- Ids1 = maps:filter(fun (_ClientId, Pid) -> node(Pid) =/= Node end, Ids),
+ Ids1 = maps:filter(fun(_ClientId, Pid) -> node(Pid) =/= Node end, Ids),
Delta = maps:keys(Ids) -- maps:keys(Ids1),
- Effects = lists:foldl(fun (ClientId, Acc) ->
- Pid = maps:get(ClientId, Ids),
- [
- {demonitor, process, Pid},
- {mod_call, ?MODULE, notify_connection, [Pid, decommission_node]},
- {mod_call, rabbit_log, debug,
- ["MQTT will remove client ID '~ts' from known "
- "as its node has been decommissioned", [ClientId]]}
- ] ++ Acc
- end, [], Delta),
+ Effects = lists:foldl(
+ fun(ClientId, Acc) ->
+ Pid = maps:get(ClientId, Ids),
+ [
+ {demonitor, process, Pid},
+ {mod_call, ?MODULE, notify_connection, [Pid, decommission_node]},
+ {mod_call, rabbit_log, debug, [
+ "MQTT will remove client ID '~ts' from known "
+ "as its node has been decommissioned",
+ [ClientId]
+ ]}
+ ] ++ Acc
+ end,
+ [],
+ Delta
+ ),
State = State0#machine_state{client_ids = Ids1},
{State, ok, Effects ++ snapshot_effects(Meta, State)};
-
apply(_Meta, Unknown, State) ->
logger:error("MQTT Raft state machine received an unknown command ~tp", [Unknown]),
{State, {error, {unknown_command, Unknown}}, []}.
@@ -127,7 +141,7 @@ state_enter(_, _) ->
%% Avoids blocking the Raft leader.
-spec notify_connection(pid(), duplicate_id | decommission_node) -> pid().
notify_connection(Pid, Reason) ->
- spawn(fun() -> gen_server2:cast(Pid, Reason) end).
+ spawn(fun() -> gen_server2:cast(Pid, Reason) end).
-spec snapshot_effects(map(), state()) -> ra_machine:effects().
snapshot_effects(#{index := RaftIdx}, State) ->
diff --git a/deps/rabbitmq_mqtt/src/mqtt_node.erl b/deps/rabbitmq_mqtt/src/mqtt_node.erl
index a6442fa85b..8a3c850b78 100644
--- a/deps/rabbitmq_mqtt/src/mqtt_node.erl
+++ b/deps/rabbitmq_mqtt/src/mqtt_node.erl
@@ -6,8 +6,15 @@
%%
-module(mqtt_node).
--export([start/0, node_id/0, server_id/0, all_node_ids/0, leave/1, trigger_election/0,
- delete/1]).
+-export([
+ start/0,
+ node_id/0,
+ server_id/0,
+ all_node_ids/0,
+ leave/1,
+ trigger_election/0,
+ delete/1
+]).
-define(ID_NAME, mqtt_node).
-define(START_TIMEOUT, 100_000).
@@ -25,8 +32,11 @@ server_id(Node) ->
{?ID_NAME, Node}.
all_node_ids() ->
- [server_id(N) || N <- rabbit_nodes:all(),
- can_participate_in_clientid_tracking(N)].
+ [
+ server_id(N)
+ || N <- rabbit_nodes:all(),
+ can_participate_in_clientid_tracking(N)
+ ].
start() ->
%% 3s to 6s randomized
@@ -40,34 +50,41 @@ start(Delay, AttemptsLeft) ->
NodeId = server_id(),
Nodes = compatible_peer_servers(),
case ra_directory:uid_of(?RA_SYSTEM, ?ID_NAME) of
- undefined ->
- case Nodes of
- [] ->
- %% Since cluster members are not known ahead of time and initial boot can be happening in parallel,
- %% we wait and check a few times (up to a few seconds) to see if we can discover any peers to
- %% join before forming a cluster. This reduces the probability of N independent clusters being
- %% formed in the common scenario of N nodes booting in parallel e.g. because they were started
- %% at the same time by a deployment tool.
- %%
- %% This scenario does not guarantee single cluster formation but without knowing the list of members
- %% ahead of time, this is a best effort workaround. Multi-node consensus is apparently hard
- %% to achieve without having consensus around expected cluster members.
- rabbit_log:info("MQTT: will wait for ~tp more ms for cluster members to join before triggering a Raft leader election", [Delay]),
- timer:sleep(Delay),
- start(Delay, AttemptsLeft - 1);
- Peers ->
- %% Trigger an election.
- %% This is required when we start a node for the first time.
- %% Using default timeout because it supposed to reply fast.
- rabbit_log:info("MQTT: discovered ~tp cluster peers that support client ID tracking", [length(Peers)]),
- ok = start_server(),
- _ = join_peers(NodeId, Peers),
- ra:trigger_election(NodeId, ?RA_OPERATION_TIMEOUT)
- end;
- _ ->
- _ = join_peers(NodeId, Nodes),
- ok = ra:restart_server(?RA_SYSTEM, NodeId),
- ra:trigger_election(NodeId, ?RA_OPERATION_TIMEOUT)
+ undefined ->
+ case Nodes of
+ [] ->
+ %% Since cluster members are not known ahead of time and initial boot can be happening in parallel,
+ %% we wait and check a few times (up to a few seconds) to see if we can discover any peers to
+ %% join before forming a cluster. This reduces the probability of N independent clusters being
+ %% formed in the common scenario of N nodes booting in parallel e.g. because they were started
+ %% at the same time by a deployment tool.
+ %%
+ %% This scenario does not guarantee single cluster formation but without knowing the list of members
+ %% ahead of time, this is a best effort workaround. Multi-node consensus is apparently hard
+ %% to achieve without having consensus around expected cluster members.
+ rabbit_log:info(
+ "MQTT: will wait for ~tp more ms for cluster members to join before triggering a Raft leader election",
+ [Delay]
+ ),
+ timer:sleep(Delay),
+ start(Delay, AttemptsLeft - 1);
+ Peers ->
+ %% Trigger an election.
+ %% This is required when we start a node for the first time.
+ %% Using default timeout because it supposed to reply fast.
+ rabbit_log:info(
+ "MQTT: discovered ~tp cluster peers that support client ID tracking", [
+ length(Peers)
+ ]
+ ),
+ ok = start_server(),
+ _ = join_peers(NodeId, Peers),
+ ra:trigger_election(NodeId, ?RA_OPERATION_TIMEOUT)
+ end;
+ _ ->
+ _ = join_peers(NodeId, Nodes),
+ ok = ra:restart_server(?RA_SYSTEM, NodeId),
+ ra:trigger_election(NodeId, ?RA_OPERATION_TIMEOUT)
end.
compatible_peer_servers() ->
@@ -78,14 +95,15 @@ start_server() ->
Nodes = compatible_peer_servers(),
UId = ra:new_uid(ra_lib:to_binary(?ID_NAME)),
Timeout = application:get_env(kernel, net_ticktime, 60) + 5,
- Conf = #{cluster_name => ?ID_NAME,
- id => NodeId,
- uid => UId,
- friendly_name => atom_to_list(?ID_NAME),
- initial_members => Nodes,
- log_init_args => #{uid => UId},
- tick_timeout => Timeout,
- machine => {module, mqtt_machine, #{}}
+ Conf = #{
+ cluster_name => ?ID_NAME,
+ id => NodeId,
+ uid => UId,
+ friendly_name => atom_to_list(?ID_NAME),
+ initial_members => Nodes,
+ log_init_args => #{uid => UId},
+ tick_timeout => Timeout,
+ machine => {module, mqtt_machine, #{}}
},
ra:start_server(?RA_SYSTEM, Conf).
@@ -103,11 +121,13 @@ join_peers(NodeId, Nodes, RetriesLeft) ->
case ra:members(Nodes, ?START_TIMEOUT) of
{ok, Members, _} ->
case lists:member(NodeId, Members) of
- true -> ok;
+ true -> ok;
false -> ra:add_member(Members, NodeId)
end;
{timeout, _} ->
- rabbit_log:debug("MQTT: timed out contacting cluster peers, %s retries left", [RetriesLeft]),
+ rabbit_log:debug("MQTT: timed out contacting cluster peers, %s retries left", [
+ RetriesLeft
+ ]),
timer:sleep(?RETRY_INTERVAL),
join_peers(NodeId, Nodes, RetriesLeft - 1);
Err ->
@@ -128,12 +148,12 @@ leave(Node) ->
can_participate_in_clientid_tracking(Node) ->
case rpc:call(Node, mqtt_machine, module_info, []) of
{badrpc, _} -> false;
- _ -> true
+ _ -> true
end.
-spec delete(Args) -> Ret when
- Args :: rabbit_feature_flags:enable_callback_args(),
- Ret :: rabbit_feature_flags:enable_callback_ret().
+ Args :: rabbit_feature_flags:enable_callback_args(),
+ Ret :: rabbit_feature_flags:enable_callback_ret().
delete(_) ->
RaNodes = all_node_ids(),
Nodes = lists:map(fun({_, N}) -> N end, RaNodes),
@@ -151,12 +171,13 @@ delete(_) ->
{ok, _Leader} ->
rabbit_log:info("Successfully deleted Ra cluster ~s", [?ID_NAME]),
ok;
- {error, _} = Err ->
+ {error, _} = Err ->
rabbit_log:info("Failed to delete Ra cluster ~s: ~p", [?ID_NAME, Err]),
Err
- catch exit:{{shutdown, delete}, _Stacktrace} ->
- rabbit_log:info("Ra cluster ~s already being deleted", [?ID_NAME]),
- ok
+ catch
+ exit:{{shutdown, delete}, _Stacktrace} ->
+ rabbit_log:info("Ra cluster ~s already being deleted", [?ID_NAME]),
+ ok
end
after
true = global:del_lock(LockId, Nodes),
diff --git a/deps/rabbitmq_mqtt/src/rabbit_mqtt.erl b/deps/rabbitmq_mqtt/src/rabbit_mqtt.erl
index 5fb1861255..6e681ea3d2 100644
--- a/deps/rabbitmq_mqtt/src/rabbit_mqtt.erl
+++ b/deps/rabbitmq_mqtt/src/rabbit_mqtt.erl
@@ -13,11 +13,13 @@
-include_lib("stdlib/include/assert.hrl").
-export([start/2, stop/1]).
--export([emit_connection_info_all/4,
- emit_connection_info_local/3,
- close_local_client_connections/1,
- %% Exported for tests, but could also be used for debugging.
- local_connection_pids/0]).
+-export([
+ emit_connection_info_all/4,
+ emit_connection_info_local/3,
+ close_local_client_connections/1,
+ %% Exported for tests, but could also be used for debugging.
+ local_connection_pids/0
+]).
start(normal, []) ->
init_global_counters(),
@@ -31,10 +33,11 @@ start(normal, []) ->
ok
end,
Result = rabbit_mqtt_sup:start_link({Listeners, SslListeners}, []),
- EMPid = case rabbit_event:start_link() of
- {ok, Pid} -> Pid;
- {error, {already_started, Pid}} -> Pid
- end,
+ EMPid =
+ case rabbit_event:start_link() of
+ {ok, Pid} -> Pid;
+ {error, {already_started, Pid}} -> Pid
+ end,
gen_event:add_handler(EMPid, rabbit_mqtt_internal_event_handler, []),
Result.
@@ -52,9 +55,15 @@ emit_connection_info_all(Nodes, Items, Ref, AggregatorPid) ->
%% remaining nodes, we send back 'finished' so that the CLI does not time out.
[AggregatorPid ! {Ref, finished} || _ <- lists:seq(1, length(Nodes) - 1)];
false ->
- Pids = [spawn_link(Node, ?MODULE, emit_connection_info_local,
- [Items, Ref, AggregatorPid])
- || Node <- Nodes],
+ Pids = [
+ spawn_link(
+ Node,
+ ?MODULE,
+ emit_connection_info_local,
+ [Items, Ref, AggregatorPid]
+ )
+ || Node <- Nodes
+ ],
rabbit_control_misc:await_emitters_termination(Pids)
end.
@@ -65,17 +74,23 @@ emit_connection_info_local(Items, Ref, AggregatorPid) ->
emit_connection_info(Items, Ref, AggregatorPid, Pids) ->
rabbit_control_misc:emitting_map_with_exit_handler(
- AggregatorPid, Ref,
- fun(Pid) ->
- rabbit_mqtt_reader:info(Pid, Items)
- end, Pids).
+ AggregatorPid,
+ Ref,
+ fun(Pid) ->
+ rabbit_mqtt_reader:info(Pid, Items)
+ end,
+ Pids
+ ).
-spec close_local_client_connections(string() | binary()) -> {'ok', non_neg_integer()}.
close_local_client_connections(Reason) ->
Pids = local_connection_pids(),
- lists:foreach(fun(Pid) ->
- rabbit_mqtt_reader:close_connection(Pid, Reason)
- end, Pids),
+ lists:foreach(
+ fun(Pid) ->
+ rabbit_mqtt_reader:close_connection(Pid, Reason)
+ end,
+ Pids
+ ),
{ok, length(Pids)}.
-spec local_connection_pids() -> [pid()].
@@ -86,9 +101,12 @@ local_connection_pids() ->
lists:filter(fun(Pid) -> node(Pid) =:= node() end, AllPids);
false ->
PgScope = persistent_term:get(?PG_SCOPE),
- lists:flatmap(fun(Group) ->
- pg:get_local_members(PgScope, Group)
- end, pg:which_groups(PgScope))
+ lists:flatmap(
+ fun(Group) ->
+ pg:get_local_members(PgScope, Group)
+ end,
+ pg:which_groups(PgScope)
+ )
end.
init_global_counters() ->
diff --git a/deps/rabbitmq_mqtt/src/rabbit_mqtt_collector.erl b/deps/rabbitmq_mqtt/src/rabbit_mqtt_collector.erl
index 5b5050d64e..438b6cf9f1 100644
--- a/deps/rabbitmq_mqtt/src/rabbit_mqtt_collector.erl
+++ b/deps/rabbitmq_mqtt/src/rabbit_mqtt_collector.erl
@@ -9,8 +9,13 @@
-include("mqtt_machine.hrl").
--export([register/2, register/3, unregister/2,
- list/0, list_pids/0, leave/1]).
+-export([
+ register/2, register/3,
+ unregister/2,
+ list/0,
+ list_pids/0,
+ leave/1
+]).
%%----------------------------------------------------------------------------
-spec register(term(), pid()) -> {ok, reference()} | {error, term()}.
@@ -21,7 +26,7 @@ register(ClientId, Pid) ->
case ra:members(NodeId) of
{ok, _, Leader} ->
register(Leader, ClientId, Pid);
- _ = Error ->
+ _ = Error ->
Error
end;
Leader ->
@@ -60,25 +65,31 @@ list(QF) ->
undefined ->
NodeIds = mqtt_node:all_node_ids(),
case ra:leader_query(NodeIds, QF) of
- {ok, {_, Result}, _} -> Result;
- {timeout, _} ->
- rabbit_log:debug("~ts:list/1 leader query timed out",
- [?MODULE]),
+ {ok, {_, Result}, _} ->
+ Result;
+ {timeout, _} ->
+ rabbit_log:debug(
+ "~ts:list/1 leader query timed out",
+ [?MODULE]
+ ),
[]
end;
Leader ->
case ra:leader_query(Leader, QF) of
- {ok, {_, Result}, _} -> Result;
+ {ok, {_, Result}, _} ->
+ Result;
{error, _} ->
[];
- {timeout, _} ->
- rabbit_log:debug("~ts:list/1 leader query timed out",
- [?MODULE]),
+ {timeout, _} ->
+ rabbit_log:debug(
+ "~ts:list/1 leader query timed out",
+ [?MODULE]
+ ),
[]
end
end.
--spec leave(binary()) -> ok | timeout | nodedown.
+-spec leave(binary()) -> ok | timeout | nodedown.
leave(NodeBin) ->
Node = binary_to_atom(NodeBin, utf8),
ServerId = mqtt_node:server_id(),
diff --git a/deps/rabbitmq_mqtt/src/rabbit_mqtt_confirms.erl b/deps/rabbitmq_mqtt/src/rabbit_mqtt_confirms.erl
index a31c94bd16..df56b14861 100644
--- a/deps/rabbitmq_mqtt/src/rabbit_mqtt_confirms.erl
+++ b/deps/rabbitmq_mqtt/src/rabbit_mqtt_confirms.erl
@@ -10,13 +10,15 @@
-include("rabbit_mqtt_packet.hrl").
-compile({no_auto_import, [size/1]}).
--export([init/0,
- insert/3,
- confirm/3,
- reject/2,
- remove_queue/2,
- size/1,
- contains/2]).
+-export([
+ init/0,
+ insert/3,
+ confirm/3,
+ reject/2,
+ remove_queue/2,
+ size/1,
+ contains/2
+]).
%% As done in OTP's sets module:
%% Empty list is cheaper to serialize than atom.
@@ -39,26 +41,32 @@ contains(PktId, State) ->
maps:is_key(PktId, State).
-spec insert(packet_id(), [queue_name()], state()) -> state().
-insert(PktId, QNames, State)
- when is_integer(PktId) andalso
- PktId > 0 andalso
- not is_map_key(PktId, State) ->
+insert(PktId, QNames, State) when
+ is_integer(PktId) andalso
+ PktId > 0 andalso
+ not is_map_key(PktId, State)
+->
QMap = maps:from_keys(QNames, ?VALUE),
maps:put(PktId, QMap, State).
-spec confirm([packet_id()], queue_name(), state()) ->
{[packet_id()], state()}.
confirm(PktIds, QName, State0) ->
- {L0, State} = lists:foldl(fun(PktId, Acc) ->
- confirm_one(PktId, QName, Acc)
- end, {[], State0}, PktIds),
+ {L0, State} = lists:foldl(
+ fun(PktId, Acc) ->
+ confirm_one(PktId, QName, Acc)
+ end,
+ {[], State0},
+ PktIds
+ ),
L = lists:reverse(L0),
{L, State}.
-spec reject(packet_id(), state()) ->
{ok, state()} | {error, not_found}.
-reject(PktId, State0)
- when is_integer(PktId) ->
+reject(PktId, State0) when
+ is_integer(PktId)
+->
case maps:take(PktId, State0) of
{_, State} ->
{ok, State};
@@ -71,24 +79,31 @@ reject(PktId, State0)
{[packet_id()], state()}.
remove_queue(QName, State) ->
PktIds = maps:fold(
- fun(PktId, QMap, PktIds)
- when is_map_key(QName, QMap) ->
- [PktId | PktIds];
- (_, _, PktIds) ->
- PktIds
- end, [], State),
+ fun
+ (PktId, QMap, PktIds) when
+ is_map_key(QName, QMap)
+ ->
+ [PktId | PktIds];
+ (_, _, PktIds) ->
+ PktIds
+ end,
+ [],
+ State
+ ),
confirm(lists:sort(PktIds), QName, State).
%% INTERNAL
-confirm_one(PktId, QName, {PktIds, State0})
- when is_integer(PktId) ->
+confirm_one(PktId, QName, {PktIds, State0}) when
+ is_integer(PktId)
+->
case maps:take(PktId, State0) of
- {QMap0, State1}
- when is_map_key(QName, QMap0)
- andalso map_size(QMap0) =:= 1 ->
+ {QMap0, State1} when
+ is_map_key(QName, QMap0) andalso
+ map_size(QMap0) =:= 1
+ ->
%% last queue confirm
- {[PktId| PktIds], State1};
+ {[PktId | PktIds], State1};
{QMap0, State1} ->
QMap = maps:remove(QName, QMap0),
State = maps:put(PktId, QMap, State1),
diff --git a/deps/rabbitmq_mqtt/src/rabbit_mqtt_ff.erl b/deps/rabbitmq_mqtt/src/rabbit_mqtt_ff.erl
index 2432cc2ac5..e132128527 100644
--- a/deps/rabbitmq_mqtt/src/rabbit_mqtt_ff.erl
+++ b/deps/rabbitmq_mqtt/src/rabbit_mqtt_ff.erl
@@ -12,17 +12,19 @@
-export([track_client_id_in_ra/0]).
-rabbit_feature_flag(
- {?QUEUE_TYPE_QOS_0,
- #{desc => "Support pseudo queue type for MQTT QoS 0 subscribers omitting a queue process",
- stability => stable
- }}).
+ {?QUEUE_TYPE_QOS_0, #{
+ desc => "Support pseudo queue type for MQTT QoS 0 subscribers omitting a queue process",
+ stability => stable
+ }}
+).
-rabbit_feature_flag(
- {delete_ra_cluster_mqtt_node,
- #{desc => "Delete Ra cluster 'mqtt_node' since MQTT client IDs are tracked locally",
- stability => stable,
- callbacks => #{enable => {mqtt_node, delete}}
- }}).
+ {delete_ra_cluster_mqtt_node, #{
+ desc => "Delete Ra cluster 'mqtt_node' since MQTT client IDs are tracked locally",
+ stability => stable,
+ callbacks => #{enable => {mqtt_node, delete}}
+ }}
+).
-spec track_client_id_in_ra() -> boolean().
track_client_id_in_ra() ->
diff --git a/deps/rabbitmq_mqtt/src/rabbit_mqtt_internal_event_handler.erl b/deps/rabbitmq_mqtt/src/rabbit_mqtt_internal_event_handler.erl
index 711c065acc..c01f4cc5b3 100644
--- a/deps/rabbitmq_mqtt/src/rabbit_mqtt_internal_event_handler.erl
+++ b/deps/rabbitmq_mqtt/src/rabbit_mqtt_internal_event_handler.erl
@@ -28,7 +28,9 @@ handle_event({event, vhost_deleted, Info, _, _}, ?STATE) ->
{ok, ?STATE};
handle_event({event, maintenance_connections_closed, _Info, _, _}, ?STATE) ->
%% we should close our connections
- {ok, NConnections} = rabbit_mqtt:close_local_client_connections("node is being put into maintenance mode"),
+ {ok, NConnections} = rabbit_mqtt:close_local_client_connections(
+ "node is being put into maintenance mode"
+ ),
rabbit_log:warning("Closed ~b local MQTT client connections", [NConnections]),
{ok, ?STATE};
handle_event(_Event, ?STATE) ->
diff --git a/deps/rabbitmq_mqtt/src/rabbit_mqtt_keepalive.erl b/deps/rabbitmq_mqtt/src/rabbit_mqtt_keepalive.erl
index 6b7b94b54c..cadfcffdad 100644
--- a/deps/rabbitmq_mqtt/src/rabbit_mqtt_keepalive.erl
+++ b/deps/rabbitmq_mqtt/src/rabbit_mqtt_keepalive.erl
@@ -1,23 +1,26 @@
-module(rabbit_mqtt_keepalive).
--export([init/0,
- start/2,
- handle/2,
- start_timer/1,
- cancel_timer/1,
- interval_secs/1]).
+-export([
+ init/0,
+ start/2,
+ handle/2,
+ start_timer/1,
+ cancel_timer/1,
+ interval_secs/1
+]).
-export_type([state/0]).
-record(state, {
- %% Keep Alive value as sent in the CONNECT packet.
- interval_secs :: pos_integer(),
- timer :: reference(),
- socket :: inet:socket(),
- recv_oct :: non_neg_integer(),
- received :: boolean()}).
+ %% Keep Alive value as sent in the CONNECT packet.
+ interval_secs :: pos_integer(),
+ timer :: reference(),
+ socket :: inet:socket(),
+ recv_oct :: non_neg_integer(),
+ received :: boolean()
+}).
--opaque(state() :: disabled | #state{}).
+-opaque state() :: disabled | #state{}.
-spec init() -> state().
init() ->
@@ -26,8 +29,9 @@ init() ->
-spec start(IntervalSeconds :: non_neg_integer(), inet:socket()) -> ok.
start(0, _Sock) ->
ok;
-start(Seconds, Sock)
- when is_integer(Seconds) andalso Seconds > 0 ->
+start(Seconds, Sock) when
+ is_integer(Seconds) andalso Seconds > 0
+->
self() ! {keepalive, {init, Seconds, Sock}},
ok.
@@ -36,20 +40,28 @@ start(Seconds, Sock)
handle({init, IntervalSecs, Sock}, _State) ->
case rabbit_net:getstat(Sock, [recv_oct]) of
{ok, [{recv_oct, RecvOct}]} ->
- {ok, #state{interval_secs = IntervalSecs,
- timer = start_timer0(IntervalSecs),
- socket = Sock,
- recv_oct = RecvOct,
- received = true}};
+ {ok, #state{
+ interval_secs = IntervalSecs,
+ timer = start_timer0(IntervalSecs),
+ socket = Sock,
+ recv_oct = RecvOct,
+ received = true
+ }};
{error, _} = Err ->
Err
end;
-handle(check, State = #state{socket = Sock,
- recv_oct = SameRecvOct,
- received = ReceivedPreviously}) ->
+handle(
+ check,
+ State = #state{
+ socket = Sock,
+ recv_oct = SameRecvOct,
+ received = ReceivedPreviously
+ }
+) ->
case rabbit_net:getstat(Sock, [recv_oct]) of
- {ok, [{recv_oct, SameRecvOct}]}
- when ReceivedPreviously ->
+ {ok, [{recv_oct, SameRecvOct}]} when
+ ReceivedPreviously
+ ->
%% Did not receive from socket for the 1st time.
{ok, start_timer(State#state{received = false})};
{ok, [{recv_oct, SameRecvOct}]} ->
@@ -57,8 +69,11 @@ handle(check, State = #state{socket = Sock,
{error, timeout};
{ok, [{recv_oct, NewRecvOct}]} ->
%% Received from socket.
- {ok, start_timer(State#state{recv_oct = NewRecvOct,
- received = true})};
+ {ok,
+ start_timer(State#state{
+ recv_oct = NewRecvOct,
+ received = true
+ })};
{error, _} = Err ->
Err
end.
@@ -74,10 +89,13 @@ start_timer0(KeepAliveSeconds) ->
erlang:send_after(timer_ms(KeepAliveSeconds), self(), {keepalive, check}).
-spec cancel_timer(state()) -> state().
-cancel_timer(#state{timer = Ref} = State)
- when is_reference(Ref) ->
- ok = erlang:cancel_timer(Ref, [{async, true},
- {info, false}]),
+cancel_timer(#state{timer = Ref} = State) when
+ is_reference(Ref)
+->
+ ok = erlang:cancel_timer(Ref, [
+ {async, true},
+ {info, false}
+ ]),
State;
cancel_timer(disabled) ->
disabled.
diff --git a/deps/rabbitmq_mqtt/src/rabbit_mqtt_packet.erl b/deps/rabbitmq_mqtt/src/rabbit_mqtt_packet.erl
index ededed8c5b..bcd31d3529 100644
--- a/deps/rabbitmq_mqtt/src/rabbit_mqtt_packet.erl
+++ b/deps/rabbitmq_mqtt/src/rabbit_mqtt_packet.erl
@@ -24,119 +24,148 @@
initial_state() -> none.
-spec parse(binary(), state()) ->
- {more, state()} |
- {ok, mqtt_packet(), binary()} |
- {error, any()}.
+ {more, state()}
+ | {ok, mqtt_packet(), binary()}
+ | {error, any()}.
parse(<<>>, none) ->
{more, fun(Bin) -> parse(Bin, none) end};
parse(<<MessageType:4, Dup:1, QoS:2, Retain:1, Rest/binary>>, none) ->
- parse_remaining_len(Rest, #mqtt_packet_fixed{ type = MessageType,
- dup = bool(Dup),
- qos = QoS,
- retain = bool(Retain) });
-parse(Bin, Cont) -> Cont(Bin).
+ parse_remaining_len(Rest, #mqtt_packet_fixed{
+ type = MessageType,
+ dup = bool(Dup),
+ qos = QoS,
+ retain = bool(Retain)
+ });
+parse(Bin, Cont) ->
+ Cont(Bin).
parse_remaining_len(<<>>, Fixed) ->
{more, fun(Bin) -> parse_remaining_len(Bin, Fixed) end};
parse_remaining_len(Rest, Fixed) ->
parse_remaining_len(Rest, Fixed, 1, 0).
-parse_remaining_len(_Bin, _Fixed, _Multiplier, Length)
- when Length > ?MAX_LEN ->
+parse_remaining_len(_Bin, _Fixed, _Multiplier, Length) when
+ Length > ?MAX_LEN
+->
{error, invalid_mqtt_packet_len};
parse_remaining_len(<<>>, Fixed, Multiplier, Length) ->
{more, fun(Bin) -> parse_remaining_len(Bin, Fixed, Multiplier, Length) end};
parse_remaining_len(<<1:1, Len:7, Rest/binary>>, Fixed, Multiplier, Value) ->
parse_remaining_len(Rest, Fixed, Multiplier * ?HIGHBIT, Value + Len * Multiplier);
-parse_remaining_len(<<0:1, Len:7, Rest/binary>>, Fixed, Multiplier, Value) ->
+parse_remaining_len(<<0:1, Len:7, Rest/binary>>, Fixed, Multiplier, Value) ->
parse_packet(Rest, Fixed, Value + Len * Multiplier).
-parse_packet(Bin, #mqtt_packet_fixed{ type = Type,
- qos = Qos } = Fixed, Length)
- when Length =< ?MAX_LEN ->
+parse_packet(
+ Bin,
+ #mqtt_packet_fixed{
+ type = Type,
+ qos = Qos
+ } = Fixed,
+ Length
+) when
+ Length =< ?MAX_LEN
+->
case {Type, Bin} of
{?CONNECT, <<PacketBin:Length/binary, Rest/binary>>} ->
{ProtoName, Rest1} = parse_utf(PacketBin),
- <<ProtoVersion : 8, Rest2/binary>> = Rest1,
- <<UsernameFlag : 1,
- PasswordFlag : 1,
- WillRetain : 1,
- WillQos : 2,
- WillFlag : 1,
- CleanSession : 1,
- _Reserved : 1,
- KeepAlive : 16/big,
- Rest3/binary>> = Rest2,
- {ClientId, Rest4} = parse_utf(Rest3),
+ <<ProtoVersion:8, Rest2/binary>> = Rest1,
+ <<UsernameFlag:1, PasswordFlag:1, WillRetain:1, WillQos:2, WillFlag:1, CleanSession:1,
+ _Reserved:1, KeepAlive:16/big, Rest3/binary>> = Rest2,
+ {ClientId, Rest4} = parse_utf(Rest3),
{WillTopic, Rest5} = parse_utf(Rest4, WillFlag),
- {WillMsg, Rest6} = parse_msg(Rest5, WillFlag),
- {UserName, Rest7} = parse_utf(Rest6, UsernameFlag),
- {PasssWord, <<>>} = parse_utf(Rest7, PasswordFlag),
+ {WillMsg, Rest6} = parse_msg(Rest5, WillFlag),
+ {UserName, Rest7} = parse_utf(Rest6, UsernameFlag),
+ {PasssWord, <<>>} = parse_utf(Rest7, PasswordFlag),
case protocol_name_approved(ProtoVersion, ProtoName) of
true ->
- wrap(Fixed,
- #mqtt_packet_connect{
- proto_ver = ProtoVersion,
- will_retain = bool(WillRetain),
- will_qos = WillQos,
- will_flag = bool(WillFlag),
- clean_sess = bool(CleanSession),
- keep_alive = KeepAlive,
- client_id = ClientId,
- will_topic = WillTopic,
- will_msg = WillMsg,
- username = UserName,
- password = PasssWord}, Rest);
- false ->
+ wrap(
+ Fixed,
+ #mqtt_packet_connect{
+ proto_ver = ProtoVersion,
+ will_retain = bool(WillRetain),
+ will_qos = WillQos,
+ will_flag = bool(WillFlag),
+ clean_sess = bool(CleanSession),
+ keep_alive = KeepAlive,
+ client_id = ClientId,
+ will_topic = WillTopic,
+ will_msg = WillMsg,
+ username = UserName,
+ password = PasssWord
+ },
+ Rest
+ );
+ false ->
{error, protocol_header_corrupt}
end;
{?PUBLISH, <<PacketBin:Length/binary, Rest/binary>>} ->
{TopicName, Rest1} = parse_utf(PacketBin),
- {PacketId, Payload} = case Qos of
- 0 -> {undefined, Rest1};
- _ -> <<M:16/big, R/binary>> = Rest1,
- {M, R}
- end,
- wrap(Fixed, #mqtt_packet_publish { topic_name = TopicName,
- packet_id = PacketId },
- Payload, Rest);
+ {PacketId, Payload} =
+ case Qos of
+ 0 ->
+ {undefined, Rest1};
+ _ ->
+ <<M:16/big, R/binary>> = Rest1,
+ {M, R}
+ end,
+ wrap(
+ Fixed,
+ #mqtt_packet_publish{
+ topic_name = TopicName,
+ packet_id = PacketId
+ },
+ Payload,
+ Rest
+ );
{?PUBACK, <<PacketBin:Length/binary, Rest/binary>>} ->
<<PacketId:16/big>> = PacketBin,
- wrap(Fixed, #mqtt_packet_publish { packet_id = PacketId }, Rest);
- {Subs, <<PacketBin:Length/binary, Rest/binary>>}
- when Subs =:= ?SUBSCRIBE orelse Subs =:= ?UNSUBSCRIBE ->
+ wrap(Fixed, #mqtt_packet_publish{packet_id = PacketId}, Rest);
+ {Subs, <<PacketBin:Length/binary, Rest/binary>>} when
+ Subs =:= ?SUBSCRIBE orelse Subs =:= ?UNSUBSCRIBE
+ ->
1 = Qos,
<<PacketId:16/big, Rest1/binary>> = PacketBin,
Topics = parse_topics(Subs, Rest1, []),
- wrap(Fixed, #mqtt_packet_subscribe { packet_id = PacketId,
- topic_table = Topics }, Rest);
- {Minimal, Rest}
- when Minimal =:= ?DISCONNECT orelse Minimal =:= ?PINGREQ ->
+ wrap(
+ Fixed,
+ #mqtt_packet_subscribe{
+ packet_id = PacketId,
+ topic_table = Topics
+ },
+ Rest
+ );
+ {Minimal, Rest} when
+ Minimal =:= ?DISCONNECT orelse Minimal =:= ?PINGREQ
+ ->
Length = 0,
wrap(Fixed, Rest);
- {_, TooShortBin}
- when byte_size(TooShortBin) < Length ->
+ {_, TooShortBin} when
+ byte_size(TooShortBin) < Length
+ ->
{more, fun(BinMore) ->
- parse_packet(<<TooShortBin/binary, BinMore/binary>>,
- Fixed, Length)
- end}
+ parse_packet(
+ <<TooShortBin/binary, BinMore/binary>>,
+ Fixed,
+ Length
+ )
+ end}
end.
parse_topics(_, <<>>, Topics) ->
Topics;
parse_topics(?SUBSCRIBE = Sub, Bin, Topics) ->
{Name, <<_:6, QoS:2, Rest/binary>>} = parse_utf(Bin),
- parse_topics(Sub, Rest, [#mqtt_topic { name = Name, qos = QoS } | Topics]);
+ parse_topics(Sub, Rest, [#mqtt_topic{name = Name, qos = QoS} | Topics]);
parse_topics(?UNSUBSCRIBE = Sub, Bin, Topics) ->
{Name, <<Rest/binary>>} = parse_utf(Bin),
- parse_topics(Sub, Rest, [#mqtt_topic { name = Name } | Topics]).
+ parse_topics(Sub, Rest, [#mqtt_topic{name = Name} | Topics]).
wrap(Fixed, Variable, Payload, Rest) ->
- {ok, #mqtt_packet { variable = Variable, fixed = Fixed, payload = Payload }, Rest}.
+ {ok, #mqtt_packet{variable = Variable, fixed = Fixed, payload = Payload}, Rest}.
wrap(Fixed, Variable, Rest) ->
- {ok, #mqtt_packet { variable = Variable, fixed = Fixed }, Rest}.
+ {ok, #mqtt_packet{variable = Variable, fixed = Fixed}, Rest}.
wrap(Fixed, Rest) ->
- {ok, #mqtt_packet { fixed = Fixed }, Rest}.
+ {ok, #mqtt_packet{fixed = Fixed}, Rest}.
parse_utf(Bin, 0) ->
{undefined, Bin};
@@ -158,72 +187,109 @@ bool(1) -> true.
-spec serialise(#mqtt_packet{}, ?MQTT_PROTO_V3 | ?MQTT_PROTO_V4) ->
iodata().
-serialise(#mqtt_packet{fixed = Fixed,
- variable = Variable,
- payload = Payload}, Vsn) ->
+serialise(
+ #mqtt_packet{
+ fixed = Fixed,
+ variable = Variable,
+ payload = Payload
+ },
+ Vsn
+) ->
serialise_variable(Fixed, Variable, serialise_payload(Payload), Vsn).
serialise_payload(undefined) ->
<<>>;
-serialise_payload(P)
- when is_binary(P) orelse is_list(P) ->
+serialise_payload(P) when
+ is_binary(P) orelse is_list(P)
+->
P.
-serialise_variable(#mqtt_packet_fixed { type = ?CONNACK } = Fixed,
- #mqtt_packet_connack { session_present = SessionPresent,
- return_code = ReturnCode },
- <<>> = PayloadBin, _Vsn) ->
+serialise_variable(
+ #mqtt_packet_fixed{type = ?CONNACK} = Fixed,
+ #mqtt_packet_connack{
+ session_present = SessionPresent,
+ return_code = ReturnCode
+ },
+ <<>> = PayloadBin,
+ _Vsn
+) ->
VariableBin = <<?RESERVED:7, (opt(SessionPresent)):1, ReturnCode:8>>,
serialise_fixed(Fixed, VariableBin, PayloadBin);
-
-serialise_variable(#mqtt_packet_fixed { type = SubAck } = Fixed,
- #mqtt_packet_suback { packet_id = PacketId,
- qos_table = Qos },
- <<>> = _PayloadBin, Vsn)
- when SubAck =:= ?SUBACK orelse SubAck =:= ?UNSUBACK ->
+serialise_variable(
+ #mqtt_packet_fixed{type = SubAck} = Fixed,
+ #mqtt_packet_suback{
+ packet_id = PacketId,
+ qos_table = Qos
+ },
+ <<>> = _PayloadBin,
+ Vsn
+) when
+ SubAck =:= ?SUBACK orelse SubAck =:= ?UNSUBACK
+->
VariableBin = <<PacketId:16/big>>,
- QosBin = case Vsn of
- ?MQTT_PROTO_V3 ->
- << <<?RESERVED:6, Q:2>> || Q <- Qos >>;
- ?MQTT_PROTO_V4 ->
- %% Allow error code (0x80) in the MQTT SUBACK message.
- << <<Q:8>> || Q <- Qos >>
- end,
+ QosBin =
+ case Vsn of
+ ?MQTT_PROTO_V3 ->
+ <<<<?RESERVED:6, Q:2>> || Q <- Qos>>;
+ ?MQTT_PROTO_V4 ->
+ %% Allow error code (0x80) in the MQTT SUBACK message.
+ <<<<Q:8>> || Q <- Qos>>
+ end,
serialise_fixed(Fixed, VariableBin, QosBin);
-
-serialise_variable(#mqtt_packet_fixed { type = ?PUBLISH,
- qos = Qos } = Fixed,
- #mqtt_packet_publish { topic_name = TopicName,
- packet_id = PacketId },
- Payload, _Vsn) ->
+serialise_variable(
+ #mqtt_packet_fixed{
+ type = ?PUBLISH,
+ qos = Qos
+ } = Fixed,
+ #mqtt_packet_publish{
+ topic_name = TopicName,
+ packet_id = PacketId
+ },
+ Payload,
+ _Vsn
+) ->
TopicBin = serialise_utf(TopicName),
- PacketIdBin = case Qos of
- 0 -> <<>>;
- 1 -> <<PacketId:16/big>>
- end,
+ PacketIdBin =
+ case Qos of
+ 0 -> <<>>;
+ 1 -> <<PacketId:16/big>>
+ end,
serialise_fixed(Fixed, <<TopicBin/binary, PacketIdBin/binary>>, Payload);
-
-serialise_variable(#mqtt_packet_fixed { type = ?PUBACK } = Fixed,
- #mqtt_packet_publish { packet_id = PacketId },
- PayloadBin, _Vsn) ->
+serialise_variable(
+ #mqtt_packet_fixed{type = ?PUBACK} = Fixed,
+ #mqtt_packet_publish{packet_id = PacketId},
+ PayloadBin,
+ _Vsn
+) ->
PacketIdBin = <<PacketId:16/big>>,
serialise_fixed(Fixed, PacketIdBin, PayloadBin);
-
-serialise_variable(#mqtt_packet_fixed {} = Fixed,
- undefined,
- <<>> = _PayloadBin, _Vsn) ->
+serialise_variable(
+ #mqtt_packet_fixed{} = Fixed,
+ undefined,
+ <<>> = _PayloadBin,
+ _Vsn
+) ->
serialise_fixed(Fixed, <<>>, <<>>).
-serialise_fixed(#mqtt_packet_fixed{ type = Type,
- dup = Dup,
- qos = Qos,
- retain = Retain }, VariableBin, Payload)
- when is_integer(Type) andalso ?CONNECT =< Type andalso Type =< ?DISCONNECT ->
+serialise_fixed(
+ #mqtt_packet_fixed{
+ type = Type,
+ dup = Dup,
+ qos = Qos,
+ retain = Retain
+ },
+ VariableBin,
+ Payload
+) when
+ is_integer(Type) andalso ?CONNECT =< Type andalso Type =< ?DISCONNECT
+->
Len = size(VariableBin) + iolist_size(Payload),
true = (Len =< ?MAX_LEN),
LenBin = serialise_len(Len),
- [<<Type:4, (opt(Dup)):1, (opt(Qos)):2, (opt(Retain)):1,
- LenBin/binary, VariableBin/binary>>, Payload].
+ [
+ <<Type:4, (opt(Dup)):1, (opt(Qos)):2, (opt(Retain)):1, LenBin/binary, VariableBin/binary>>,
+ Payload
+ ].
serialise_utf(String) ->
StringBin = unicode:characters_to_binary(String),
@@ -236,9 +302,9 @@ serialise_len(N) when N =< ?LOWBITS ->
serialise_len(N) ->
<<1:1, (N rem ?HIGHBIT):7, (serialise_len(N div ?HIGHBIT))/binary>>.
-opt(undefined) -> ?RESERVED;
-opt(false) -> 0;
-opt(true) -> 1;
+opt(undefined) -> ?RESERVED;
+opt(false) -> 0;
+opt(true) -> 1;
opt(X) when is_integer(X) -> X.
protocol_name_approved(Ver, Name) ->
diff --git a/deps/rabbitmq_mqtt/src/rabbit_mqtt_processor.erl b/deps/rabbitmq_mqtt/src/rabbit_mqtt_processor.erl
index 58bccaa2f5..31c149cf5f 100644
--- a/deps/rabbitmq_mqtt/src/rabbit_mqtt_processor.erl
+++ b/deps/rabbitmq_mqtt/src/rabbit_mqtt_processor.erl
@@ -8,20 +8,31 @@
%% This module contains code that is common to MQTT and Web MQTT connections.
-module(rabbit_mqtt_processor).
--export([info/2, initial_state/2, initial_state/4,
- process_packet/2, serialise/2,
- terminate/4, handle_pre_hibernate/0,
- handle_ra_event/2, handle_down/2, handle_queue_event/2,
- proto_version_tuple/1, throttle/3, format_status/1,
- update_trace/2]).
+-export([
+ info/2,
+ initial_state/2, initial_state/4,
+ process_packet/2,
+ serialise/2,
+ terminate/4,
+ handle_pre_hibernate/0,
+ handle_ra_event/2,
+ handle_down/2,
+ handle_queue_event/2,
+ proto_version_tuple/1,
+ throttle/3,
+ format_status/1,
+ update_trace/2
+]).
%% for testing purposes
-export([get_vhost_username/1, get_vhost/3, get_vhost_from_user_mapping/2]).
-export_type([state/0]).
--import(rabbit_mqtt_util, [mqtt_to_amqp/1,
- amqp_to_mqtt/1]).
+-import(rabbit_mqtt_util, [
+ mqtt_to_amqp/1,
+ amqp_to_mqtt/1
+]).
-include_lib("kernel/include/logger.hrl").
-include_lib("rabbit_common/include/rabbit.hrl").
@@ -33,57 +44,57 @@
-define(MAX_PERMISSION_CACHE_SIZE, 12).
-define(CONSUMER_TAG, <<"mqtt">>).
--record(auth_state,
- {username :: rabbit_types:username(),
- user :: #user{},
- vhost :: rabbit_types:vhost(),
- authz_ctx :: #{binary() := binary()}
- }).
-
--record(cfg,
- {socket :: rabbit_net:socket(),
- proto_ver :: option(mqtt310 | mqtt311),
- clean_sess :: option(boolean()),
- will_msg :: option(mqtt_msg()),
- exchange :: option(rabbit_exchange:name()),
- %% Set if client has at least one subscription with QoS 1.
- queue_qos1 :: option(rabbit_amqqueue:name()),
- %% Did the client ever sent us a PUBLISH packet?
- published = false :: boolean(),
- ssl_login_name :: none | binary(),
- retainer_pid :: option(pid()),
- delivery_flow :: flow | noflow,
- trace_state :: option(rabbit_trace:state()),
- prefetch :: non_neg_integer(),
- client_id :: option(binary()),
- conn_name :: option(binary()),
- peer_addr :: inet:ip_address(),
- host :: inet:ip_address(),
- port :: inet:port_number(),
- peer_host :: inet:ip_address(),
- peer_port :: inet:port_number(),
- connected_at :: pos_integer(),
- send_fun :: fun((Packet :: tuple(), state()) -> term())
- }).
-
--record(state,
- {cfg :: #cfg{},
- queue_states = rabbit_queue_type:init() :: rabbit_queue_type:state(),
- %% Packet IDs published to queues but not yet confirmed.
- unacked_client_pubs = rabbit_mqtt_confirms:init() :: rabbit_mqtt_confirms:state(),
- %% Packet IDs published to MQTT subscribers but not yet acknowledged.
- unacked_server_pubs = #{} :: #{packet_id() => QueueMsgId :: non_neg_integer()},
- %% Packet ID of next PUBLISH packet (with QoS > 0) sent from server to client.
- %% (Not to be confused with packet IDs sent from client to server which can be the
- %% same IDs because client and server assign IDs independently of each other.)
- packet_id = 1 :: packet_id(),
- subscriptions = #{} :: #{Topic :: binary() => QoS :: ?QOS_0..?QOS_1},
- auth_state :: option(#auth_state{}),
- register_state :: option(registered | {pending, reference()}),
- %% quorum queues and streams whose soft limit has been exceeded
- queues_soft_limit_exceeded = sets:new([{version, 2}]) :: sets:set(),
- qos0_messages_dropped = 0 :: non_neg_integer()
- }).
+-record(auth_state, {
+ username :: rabbit_types:username(),
+ user :: #user{},
+ vhost :: rabbit_types:vhost(),
+ authz_ctx :: #{binary() := binary()}
+}).
+
+-record(cfg, {
+ socket :: rabbit_net:socket(),
+ proto_ver :: option(mqtt310 | mqtt311),
+ clean_sess :: option(boolean()),
+ will_msg :: option(mqtt_msg()),
+ exchange :: option(rabbit_exchange:name()),
+ %% Set if client has at least one subscription with QoS 1.
+ queue_qos1 :: option(rabbit_amqqueue:name()),
+ %% Did the client ever sent us a PUBLISH packet?
+ published = false :: boolean(),
+ ssl_login_name :: none | binary(),
+ retainer_pid :: option(pid()),
+ delivery_flow :: flow | noflow,
+ trace_state :: option(rabbit_trace:state()),
+ prefetch :: non_neg_integer(),
+ client_id :: option(binary()),
+ conn_name :: option(binary()),
+ peer_addr :: inet:ip_address(),
+ host :: inet:ip_address(),
+ port :: inet:port_number(),
+ peer_host :: inet:ip_address(),
+ peer_port :: inet:port_number(),
+ connected_at :: pos_integer(),
+ send_fun :: fun((Packet :: tuple(), state()) -> term())
+}).
+
+-record(state, {
+ cfg :: #cfg{},
+ queue_states = rabbit_queue_type:init() :: rabbit_queue_type:state(),
+ %% Packet IDs published to queues but not yet confirmed.
+ unacked_client_pubs = rabbit_mqtt_confirms:init() :: rabbit_mqtt_confirms:state(),
+ %% Packet IDs published to MQTT subscribers but not yet acknowledged.
+ unacked_server_pubs = #{} :: #{packet_id() => QueueMsgId :: non_neg_integer()},
+ %% Packet ID of next PUBLISH packet (with QoS > 0) sent from server to client.
+ %% (Not to be confused with packet IDs sent from client to server which can be the
+ %% same IDs because client and server assign IDs independently of each other.)
+ packet_id = 1 :: packet_id(),
+ subscriptions = #{} :: #{Topic :: binary() => QoS :: ?QOS_0..?QOS_1},
+ auth_state :: option(#auth_state{}),
+ register_state :: option(registered | {pending, reference()}),
+ %% quorum queues and streams whose soft limit has been exceeded
+ queues_soft_limit_exceeded = sets:new([{version, 2}]) :: sets:set(),
+ qos0_messages_dropped = 0 :: non_neg_integer()
+}).
-opaque state() :: #state{}.
@@ -91,50 +102,62 @@
state().
initial_state(Socket, ConnectionName) ->
{ok, {PeerAddr, _PeerPort}} = rabbit_net:peername(Socket),
- initial_state(Socket,
- ConnectionName,
- fun serialise_and_send_to_client/2,
- PeerAddr).
-
--spec initial_state(Socket :: any(),
- ConnectionName :: binary(),
- SendFun :: fun((mqtt_packet(), state()) -> any()),
- PeerAddr :: inet:ip_address()) ->
+ initial_state(
+ Socket,
+ ConnectionName,
+ fun serialise_and_send_to_client/2,
+ PeerAddr
+ ).
+
+-spec initial_state(
+ Socket :: any(),
+ ConnectionName :: binary(),
+ SendFun :: fun((mqtt_packet(), state()) -> any()),
+ PeerAddr :: inet:ip_address()
+) ->
state().
initial_state(Socket, ConnectionName, SendFun, PeerAddr) ->
- Flow = case rabbit_misc:get_env(rabbit, mirroring_flow_control, true) of
- true -> flow;
- false -> noflow
- end,
+ Flow =
+ case rabbit_misc:get_env(rabbit, mirroring_flow_control, true) of
+ true -> flow;
+ false -> noflow
+ end,
{ok, {PeerHost, PeerPort, Host, Port}} = rabbit_net:socket_ends(Socket, inbound),
- #state{cfg = #cfg{socket = Socket,
- conn_name = ConnectionName,
- ssl_login_name = ssl_login_name(Socket),
- send_fun = SendFun,
- prefetch = rabbit_mqtt_util:env(prefetch),
- delivery_flow = Flow,
- connected_at = os:system_time(milli_seconds),
- peer_addr = PeerAddr,
- peer_host = PeerHost,
- peer_port = PeerPort,
- host = Host,
- port = Port}}.
+ #state{
+ cfg = #cfg{
+ socket = Socket,
+ conn_name = ConnectionName,
+ ssl_login_name = ssl_login_name(Socket),
+ send_fun = SendFun,
+ prefetch = rabbit_mqtt_util:env(prefetch),
+ delivery_flow = Flow,
+ connected_at = os:system_time(milli_seconds),
+ peer_addr = PeerAddr,
+ peer_host = PeerHost,
+ peer_port = PeerPort,
+ host = Host,
+ port = Port
+ }
+ }.
-spec process_packet(mqtt_packet(), state()) ->
- {ok, state()} |
- {stop, disconnect, state()} |
- {error, Reason :: term(), state()}.
-process_packet(#mqtt_packet{fixed = #mqtt_packet_fixed{type = Type}},
- State = #state{auth_state = undefined})
- when Type =/= ?CONNECT ->
+ {ok, state()}
+ | {stop, disconnect, state()}
+ | {error, Reason :: term(), state()}.
+process_packet(
+ #mqtt_packet{fixed = #mqtt_packet_fixed{type = Type}},
+ State = #state{auth_state = undefined}
+) when
+ Type =/= ?CONNECT
+->
{error, connect_expected, State};
process_packet(Packet = #mqtt_packet{fixed = #mqtt_packet_fixed{type = Type}}, State) ->
process_request(Type, Packet, State).
-spec process_request(packet_type(), mqtt_packet(), state()) ->
- {ok, state()} |
- {stop, disconnect, state()} |
- {error, Reason :: term(), state()}.
+ {ok, state()}
+ | {stop, disconnect, state()}
+ | {error, Reason :: term(), state()}.
process_request(?CONNECT, Packet, State = #state{cfg = #cfg{socket = Socket}}) ->
%% Check whether peer closed the connection.
%% For example, this can happen when connection was blocked because of resource
@@ -145,68 +168,92 @@ process_request(?CONNECT, Packet, State = #state{cfg = #cfg{socket = Socket}}) -
_ ->
process_connect(Packet, State)
end;
-
-process_request(?PUBACK,
- #mqtt_packet{variable = #mqtt_packet_publish{packet_id = PacketId}},
- #state{unacked_server_pubs = U0,
- queue_states = QStates0,
- cfg = #cfg{queue_qos1 = QName}} = State) ->
+process_request(
+ ?PUBACK,
+ #mqtt_packet{variable = #mqtt_packet_publish{packet_id = PacketId}},
+ #state{
+ unacked_server_pubs = U0,
+ queue_states = QStates0,
+ cfg = #cfg{queue_qos1 = QName}
+ } = State
+) ->
case maps:take(PacketId, U0) of
{QMsgId, U} ->
case rabbit_queue_type:settle(QName, complete, ?CONSUMER_TAG, [QMsgId], QStates0) of
{ok, QStates, Actions} ->
message_acknowledged(QName, State),
- {ok, handle_queue_actions(Actions, State#state{unacked_server_pubs = U,
- queue_states = QStates})};
+ {ok,
+ handle_queue_actions(Actions, State#state{
+ unacked_server_pubs = U,
+ queue_states = QStates
+ })};
{protocol_error, _ErrorType, _Reason, _ReasonArgs} = Err ->
{error, Err, State}
end;
error ->
{ok, State}
end;
-
-process_request(?PUBLISH,
- Packet = #mqtt_packet{
- fixed = Fixed = #mqtt_packet_fixed{qos = ?QOS_2}},
- State) ->
+process_request(
+ ?PUBLISH,
+ Packet = #mqtt_packet{
+ fixed = Fixed = #mqtt_packet_fixed{qos = ?QOS_2}
+ },
+ State
+) ->
% Downgrade QOS_2 to QOS_1
- process_request(?PUBLISH,
- Packet#mqtt_packet{
- fixed = Fixed#mqtt_packet_fixed{qos = ?QOS_1}},
- State);
-process_request(?PUBLISH,
- #mqtt_packet{
- fixed = #mqtt_packet_fixed{qos = Qos,
- retain = Retain,
- dup = Dup },
- variable = #mqtt_packet_publish{topic_name = Topic,
- packet_id = PacketId },
- payload = Payload},
- State0 = #state{unacked_client_pubs = U,
- cfg = #cfg{retainer_pid = RPid,
- proto_ver = ProtoVer}}) ->
+ process_request(
+ ?PUBLISH,
+ Packet#mqtt_packet{
+ fixed = Fixed#mqtt_packet_fixed{qos = ?QOS_1}
+ },
+ State
+ );
+process_request(
+ ?PUBLISH,
+ #mqtt_packet{
+ fixed = #mqtt_packet_fixed{
+ qos = Qos,
+ retain = Retain,
+ dup = Dup
+ },
+ variable = #mqtt_packet_publish{
+ topic_name = Topic,
+ packet_id = PacketId
+ },
+ payload = Payload
+ },
+ State0 = #state{
+ unacked_client_pubs = U,
+ cfg = #cfg{
+ retainer_pid = RPid,
+ proto_ver = ProtoVer
+ }
+ }
+) ->
rabbit_global_counters:messages_received(ProtoVer, 1),
State = maybe_increment_publisher(State0),
Publish = fun() ->
- Msg = #mqtt_msg{retain = Retain,
- qos = Qos,
- topic = Topic,
- dup = Dup,
- packet_id = PacketId,
- payload = Payload},
- case publish_to_queues(Msg, State) of
- {ok, _} = Ok ->
- case Retain of
- false ->
- ok;
- true ->
- hand_off_to_retainer(RPid, Topic, Msg)
- end,
- Ok;
- Error ->
- Error
- end
- end,
+ Msg = #mqtt_msg{
+ retain = Retain,
+ qos = Qos,
+ topic = Topic,
+ dup = Dup,
+ packet_id = PacketId,
+ payload = Payload
+ },
+ case publish_to_queues(Msg, State) of
+ {ok, _} = Ok ->
+ case Retain of
+ false ->
+ ok;
+ true ->
+ hand_off_to_retainer(RPid, Topic, Msg)
+ end,
+ Ok;
+ Error ->
+ Error
+ end
+ end,
case Qos of
N when N > ?QOS_0 ->
rabbit_global_counters:messages_received_confirm(ProtoVer, 1),
@@ -222,157 +269,219 @@ process_request(?PUBLISH,
_ ->
publish_to_queues_with_checks(Topic, Publish, State)
end;
-
-process_request(?SUBSCRIBE,
- #mqtt_packet{
- variable = #mqtt_packet_subscribe{
- packet_id = SubscribePktId,
- topic_table = Topics},
- payload = undefined},
- #state{cfg = #cfg{send_fun = SendFun,
- retainer_pid = RPid}} = State0) ->
+process_request(
+ ?SUBSCRIBE,
+ #mqtt_packet{
+ variable = #mqtt_packet_subscribe{
+ packet_id = SubscribePktId,
+ topic_table = Topics
+ },
+ payload = undefined
+ },
+ #state{
+ cfg = #cfg{
+ send_fun = SendFun,
+ retainer_pid = RPid
+ }
+ } = State0
+) ->
?LOG_DEBUG("Received a SUBSCRIBE for topic(s) ~p", [Topics]),
{QosResponse, State1} =
- lists:foldl(
- fun(_Topic, {[?SUBACK_FAILURE | _] = L, S}) ->
- %% Once a subscription failed, mark all following subscriptions
- %% as failed instead of creating bindings because we are going
- %% to close the client connection anyway.
- {[?SUBACK_FAILURE | L], S};
- (#mqtt_topic{name = TopicName,
- qos = TopicQos},
- {L, S0}) ->
- QoS = supported_sub_qos(TopicQos),
- case maybe_replace_old_sub(TopicName, QoS, S0) of
- {ok, S1} ->
- case ensure_queue(QoS, S1) of
- {ok, Q} ->
- QName = amqqueue:get_name(Q),
- case bind(QName, TopicName, S1) of
- {ok, _Output, S2 = #state{subscriptions = Subs}} ->
- S3 = S2#state{subscriptions = maps:put(TopicName, QoS, Subs)},
- maybe_increment_consumer(S2, S3),
- case self_consumes(Q) of
- false ->
- case consume(Q, QoS, S3) of
- {ok, S4} ->
- {[QoS | L], S4};
- {error, _Reason} ->
- {[?SUBACK_FAILURE | L], S3}
- end;
- true ->
- {[QoS | L], S3}
- end;
- {error, Reason, S2} ->
- ?LOG_ERROR("Failed to bind ~s with topic ~s: ~p",
- [rabbit_misc:rs(QName), TopicName, Reason]),
- {[?SUBACK_FAILURE | L], S2}
- end;
- {error, _} ->
- {[?SUBACK_FAILURE | L], S1}
- end;
- {error, _Reason, S1} ->
- {[?SUBACK_FAILURE | L], S1}
- end
- end, {[], State0}, Topics),
+ lists:foldl(
+ fun
+ (_Topic, {[?SUBACK_FAILURE | _] = L, S}) ->
+ %% Once a subscription failed, mark all following subscriptions
+ %% as failed instead of creating bindings because we are going
+ %% to close the client connection anyway.
+ {[?SUBACK_FAILURE | L], S};
+ (
+ #mqtt_topic{
+ name = TopicName,
+ qos = TopicQos
+ },
+ {L, S0}
+ ) ->
+ QoS = supported_sub_qos(TopicQos),
+ case maybe_replace_old_sub(TopicName, QoS, S0) of
+ {ok, S1} ->
+ case ensure_queue(QoS, S1) of
+ {ok, Q} ->
+ QName = amqqueue:get_name(Q),
+ case bind(QName, TopicName, S1) of
+ {ok, _Output, S2 = #state{subscriptions = Subs}} ->
+ S3 = S2#state{
+ subscriptions = maps:put(TopicName, QoS, Subs)
+ },
+ maybe_increment_consumer(S2, S3),
+ case self_consumes(Q) of
+ false ->
+ case consume(Q, QoS, S3) of
+ {ok, S4} ->
+ {[QoS | L], S4};
+ {error, _Reason} ->
+ {[?SUBACK_FAILURE | L], S3}
+ end;
+ true ->
+ {[QoS | L], S3}
+ end;
+ {error, Reason, S2} ->
+ ?LOG_ERROR(
+ "Failed to bind ~s with topic ~s: ~p",
+ [rabbit_misc:rs(QName), TopicName, Reason]
+ ),
+ {[?SUBACK_FAILURE | L], S2}
+ end;
+ {error, _} ->
+ {[?SUBACK_FAILURE | L], S1}
+ end;
+ {error, _Reason, S1} ->
+ {[?SUBACK_FAILURE | L], S1}
+ end
+ end,
+ {[], State0},
+ Topics
+ ),
SendFun(
- #mqtt_packet{fixed = #mqtt_packet_fixed{type = ?SUBACK},
- variable = #mqtt_packet_suback{
- packet_id = SubscribePktId,
- qos_table = QosResponse}},
- State1),
+ #mqtt_packet{
+ fixed = #mqtt_packet_fixed{type = ?SUBACK},
+ variable = #mqtt_packet_suback{
+ packet_id = SubscribePktId,
+ qos_table = QosResponse
+ }
+ },
+ State1
+ ),
case QosResponse of
[?SUBACK_FAILURE | _] ->
{error, subscribe_error, State1};
_ ->
- State = lists:foldl(fun(Topic, S) ->
- maybe_send_retained_message(RPid, Topic, S)
- end, State1, Topics),
+ State = lists:foldl(
+ fun(Topic, S) ->
+ maybe_send_retained_message(RPid, Topic, S)
+ end,
+ State1,
+ Topics
+ ),
{ok, State}
end;
-
-process_request(?UNSUBSCRIBE,
- #mqtt_packet{variable = #mqtt_packet_subscribe{packet_id = PacketId,
- topic_table = Topics},
- payload = undefined},
- State0 = #state{cfg = #cfg{send_fun = SendFun}}) ->
+process_request(
+ ?UNSUBSCRIBE,
+ #mqtt_packet{
+ variable = #mqtt_packet_subscribe{
+ packet_id = PacketId,
+ topic_table = Topics
+ },
+ payload = undefined
+ },
+ State0 = #state{cfg = #cfg{send_fun = SendFun}}
+) ->
?LOG_DEBUG("Received an UNSUBSCRIBE for topic(s) ~p", [Topics]),
State = lists:foldl(
- fun(#mqtt_topic{name = TopicName}, #state{subscriptions = Subs0} = S0) ->
- case maps:take(TopicName, Subs0) of
- {QoS, Subs} ->
- QName = queue_name(QoS, S0),
- case unbind(QName, TopicName, S0) of
- {ok, _, S1} ->
- S = S1#state{subscriptions = Subs},
- maybe_decrement_consumer(S1, S),
- S;
- {error, Reason, S} ->
- ?LOG_ERROR("Failed to unbind ~s with topic ~s: ~p",
- [rabbit_misc:rs(QName), TopicName, Reason]),
- S
- end;
- error ->
- S0
- end
- end, State0, Topics),
+ fun(#mqtt_topic{name = TopicName}, #state{subscriptions = Subs0} = S0) ->
+ case maps:take(TopicName, Subs0) of
+ {QoS, Subs} ->
+ QName = queue_name(QoS, S0),
+ case unbind(QName, TopicName, S0) of
+ {ok, _, S1} ->
+ S = S1#state{subscriptions = Subs},
+ maybe_decrement_consumer(S1, S),
+ S;
+ {error, Reason, S} ->
+ ?LOG_ERROR(
+ "Failed to unbind ~s with topic ~s: ~p",
+ [rabbit_misc:rs(QName), TopicName, Reason]
+ ),
+ S
+ end;
+ error ->
+ S0
+ end
+ end,
+ State0,
+ Topics
+ ),
SendFun(
- #mqtt_packet{fixed = #mqtt_packet_fixed {type = ?UNSUBACK},
- variable = #mqtt_packet_suback{packet_id = PacketId}},
- State),
+ #mqtt_packet{
+ fixed = #mqtt_packet_fixed{type = ?UNSUBACK},
+ variable = #mqtt_packet_suback{packet_id = PacketId}
+ },
+ State
+ ),
{ok, State};
-
-process_request(?PINGREQ, #mqtt_packet{},
- State = #state{cfg = #cfg{send_fun = SendFun,
- client_id = ClientId}}) ->
+process_request(
+ ?PINGREQ,
+ #mqtt_packet{},
+ State = #state{
+ cfg = #cfg{
+ send_fun = SendFun,
+ client_id = ClientId
+ }
+ }
+) ->
?LOG_DEBUG("Received a PINGREQ, client ID: ~s", [ClientId]),
SendFun(
- #mqtt_packet{fixed = #mqtt_packet_fixed{type = ?PINGRESP}},
- State),
+ #mqtt_packet{fixed = #mqtt_packet_fixed{type = ?PINGRESP}},
+ State
+ ),
?LOG_DEBUG("Sent a PINGRESP, client ID: ~s", [ClientId]),
{ok, State};
-
process_request(?DISCONNECT, #mqtt_packet{}, State) ->
?LOG_DEBUG("Received a DISCONNECT"),
{stop, disconnect, State}.
-process_connect(#mqtt_packet{
- variable = #mqtt_packet_connect{
- username = Username,
- proto_ver = ProtoVersion,
- clean_sess = CleanSess,
- client_id = ClientId,
- keep_alive = Keepalive} = PacketConnect},
- State0 = #state{cfg = #cfg{send_fun = SendFun}}) ->
- ?LOG_DEBUG("Received a CONNECT, client ID: ~s, username: ~s, "
- "clean session: ~s, protocol version: ~p, keepalive: ~p",
- [ClientId, Username, CleanSess, ProtoVersion, Keepalive]),
+process_connect(
+ #mqtt_packet{
+ variable =
+ #mqtt_packet_connect{
+ username = Username,
+ proto_ver = ProtoVersion,
+ clean_sess = CleanSess,
+ client_id = ClientId,
+ keep_alive = Keepalive
+ } = PacketConnect
+ },
+ State0 = #state{cfg = #cfg{send_fun = SendFun}}
+) ->
+ ?LOG_DEBUG(
+ "Received a CONNECT, client ID: ~s, username: ~s, "
+ "clean session: ~s, protocol version: ~p, keepalive: ~p",
+ [ClientId, Username, CleanSess, ProtoVersion, Keepalive]
+ ),
{ReturnCode, SessPresent, State} =
- case rabbit_misc:pipeline([fun check_protocol_version/1,
- fun check_client_id/1,
- fun check_credentials/2,
- fun login/2,
- fun register_client/2,
- fun start_keepalive/2,
- fun notify_connection_created/1,
- fun init_trace/2,
- fun handle_clean_sess_qos0/2,
- fun handle_clean_sess_qos1/2,
- fun cache_subscriptions/2
- ],
- PacketConnect, State0) of
- {ok, SessPresent0, State1} ->
- {?CONNACK_ACCEPT, SessPresent0, State1};
- {error, ConnectionRefusedReturnCode, State1} ->
- {ConnectionRefusedReturnCode, false, State1}
- end,
- Response = #mqtt_packet{fixed = #mqtt_packet_fixed{type = ?CONNACK},
- variable = #mqtt_packet_connack{
- session_present = SessPresent,
- return_code = ReturnCode}},
+ case
+ rabbit_misc:pipeline(
+ [
+ fun check_protocol_version/1,
+ fun check_client_id/1,
+ fun check_credentials/2,
+ fun login/2,
+ fun register_client/2,
+ fun start_keepalive/2,
+ fun notify_connection_created/1,
+ fun init_trace/2,
+ fun handle_clean_sess_qos0/2,
+ fun handle_clean_sess_qos1/2,
+ fun cache_subscriptions/2
+ ],
+ PacketConnect,
+ State0
+ )
+ of
+ {ok, SessPresent0, State1} ->
+ {?CONNACK_ACCEPT, SessPresent0, State1};
+ {error, ConnectionRefusedReturnCode, State1} ->
+ {ConnectionRefusedReturnCode, false, State1}
+ end,
+ Response = #mqtt_packet{
+ fixed = #mqtt_packet_fixed{type = ?CONNACK},
+ variable = #mqtt_packet_connack{
+ session_present = SessPresent,
+ return_code = ReturnCode
+ }
+ },
SendFun(Response, State),
return_connack(ReturnCode, State).
@@ -384,16 +493,26 @@ check_protocol_version(#mqtt_packet_connect{proto_ver = ProtoVersion}) ->
{error, ?CONNACK_UNACCEPTABLE_PROTO_VER}
end.
-check_client_id(#mqtt_packet_connect{clean_sess = false,
- client_id = <<>>}) ->
+check_client_id(#mqtt_packet_connect{
+ clean_sess = false,
+ client_id = <<>>
+}) ->
{error, ?CONNACK_ID_REJECTED};
check_client_id(_) ->
ok.
-check_credentials(Packet = #mqtt_packet_connect{username = Username,
- password = Password},
- State = #state{cfg = #cfg{ssl_login_name = SslLoginName,
- peer_addr = PeerAddr}}) ->
+check_credentials(
+ Packet = #mqtt_packet_connect{
+ username = Username,
+ password = Password
+ },
+ State = #state{
+ cfg = #cfg{
+ ssl_login_name = SslLoginName,
+ peer_addr = PeerAddr
+ }
+ }
+) ->
Ip = list_to_binary(inet:ntoa(PeerAddr)),
case creds(Username, Password, SslLoginName) of
nocreds ->
@@ -412,15 +531,21 @@ check_credentials(Packet = #mqtt_packet_connect{username = Username,
{ok, {UserBin, PassBin, Packet}, State}
end.
-login({UserBin, PassBin,
- Packet = #mqtt_packet_connect{client_id = ClientId0,
- clean_sess = CleanSess}},
- State0 = #state{cfg = Cfg0}) ->
+login(
+ {UserBin, PassBin,
+ Packet = #mqtt_packet_connect{
+ client_id = ClientId0,
+ clean_sess = CleanSess
+ }},
+ State0 = #state{cfg = Cfg0}
+) ->
ClientId = ensure_client_id(ClientId0),
case process_login(UserBin, PassBin, ClientId, State0) of
{ok, State} ->
- Cfg = Cfg0#cfg{client_id = ClientId,
- clean_sess = CleanSess},
+ Cfg = Cfg0#cfg{
+ client_id = ClientId,
+ clean_sess = CleanSess
+ },
{ok, Packet, State#state{cfg = Cfg}};
{error, _ConnectionRefusedReturnCode, _State} = Err ->
Err
@@ -429,28 +554,39 @@ login({UserBin, PassBin,
-spec ensure_client_id(binary()) -> binary().
ensure_client_id(<<>>) ->
rabbit_data_coercion:to_binary(
- rabbit_misc:base64url(
- rabbit_guid:gen_secure()));
-ensure_client_id(ClientId)
- when is_binary(ClientId) ->
+ rabbit_misc:base64url(
+ rabbit_guid:gen_secure()
+ )
+ );
+ensure_client_id(ClientId) when
+ is_binary(ClientId)
+->
ClientId.
-register_client(Packet = #mqtt_packet_connect{proto_ver = ProtoVersion},
- State = #state{auth_state = #auth_state{vhost = VHost},
- cfg = Cfg0 = #cfg{client_id = ClientId}}) ->
+register_client(
+ Packet = #mqtt_packet_connect{proto_ver = ProtoVersion},
+ State = #state{
+ auth_state = #auth_state{vhost = VHost},
+ cfg = Cfg0 = #cfg{client_id = ClientId}
+ }
+) ->
NewProcState =
- fun(RegisterState) ->
+ fun(RegisterState) ->
rabbit_mqtt_util:register_clientid(VHost, ClientId),
RetainerPid = rabbit_mqtt_retainer_sup:child_for_vhost(VHost),
ExchangeBin = rabbit_mqtt_util:env(exchange),
ExchangeName = rabbit_misc:r(VHost, exchange, ExchangeBin),
- Cfg = Cfg0#cfg{exchange = ExchangeName,
- will_msg = make_will_msg(Packet),
- retainer_pid = RetainerPid,
- proto_ver = proto_integer_to_atom(ProtoVersion)},
- State#state{cfg = Cfg,
- register_state = RegisterState}
- end,
+ Cfg = Cfg0#cfg{
+ exchange = ExchangeName,
+ will_msg = make_will_msg(Packet),
+ retainer_pid = RetainerPid,
+ proto_ver = proto_integer_to_atom(ProtoVersion)
+ },
+ State#state{
+ cfg = Cfg,
+ register_state = RegisterState
+ }
+ end,
case rabbit_mqtt_ff:track_client_id_in_ra() of
true ->
case rabbit_mqtt_collector:register(ClientId, self()) of
@@ -458,8 +594,10 @@ register_client(Packet = #mqtt_packet_connect{proto_ver = ProtoVersion},
{ok, NewProcState({pending, Corr})};
{error, _} = Err ->
%% e.g. this node was removed from the MQTT cluster members
- ?LOG_ERROR("MQTT cannot accept a connection: client ID tracker is unavailable: ~p",
- [Err]),
+ ?LOG_ERROR(
+ "MQTT cannot accept a connection: client ID tracker is unavailable: ~p",
+ [Err]
+ ),
{error, ?CONNACK_SERVER_UNAVAILABLE}
end;
false ->
@@ -475,17 +613,25 @@ init_trace(#mqtt_packet_connect{}, State = #state{cfg = #cfg{conn_name = ConnNam
{ok, update_trace(ConnName, State)}.
-spec update_trace(binary(), state()) -> state().
-update_trace(ConnName0, State = #state{cfg = Cfg0,
- auth_state = #auth_state{vhost = VHost}}) ->
- ConnName = case rabbit_trace:enabled(VHost) of
- true ->
- ConnName0;
- false ->
- %% We won't need conn_name. Use less memmory by setting to undefined.
- undefined
- end,
- Cfg = Cfg0#cfg{conn_name = ConnName,
- trace_state = rabbit_trace:init(VHost)},
+update_trace(
+ ConnName0,
+ State = #state{
+ cfg = Cfg0,
+ auth_state = #auth_state{vhost = VHost}
+ }
+) ->
+ ConnName =
+ case rabbit_trace:enabled(VHost) of
+ true ->
+ ConnName0;
+ false ->
+ %% We won't need conn_name. Use less memmory by setting to undefined.
+ undefined
+ end,
+ Cfg = Cfg0#cfg{
+ conn_name = ConnName,
+ trace_state = rabbit_trace:init(VHost)
+ },
State#state{cfg = Cfg}.
return_connack(?CONNACK_ACCEPT, S) ->
@@ -507,13 +653,18 @@ self_consumes(Queue) ->
?QUEUE_TYPE_QOS_0 ->
false;
_ ->
- lists:any(fun(Consumer) ->
- element(1, Consumer) =:= self()
- end, rabbit_amqqueue:consumers(Queue))
+ lists:any(
+ fun(Consumer) ->
+ element(1, Consumer) =:= self()
+ end,
+ rabbit_amqqueue:consumers(Queue)
+ )
end.
-start_keepalive(#mqtt_packet_connect{keep_alive = Seconds},
- #state{cfg = #cfg{socket = Socket}}) ->
+start_keepalive(
+ #mqtt_packet_connect{keep_alive = Seconds},
+ #state{cfg = #cfg{socket = Socket}}
+) ->
ok = rabbit_mqtt_keepalive:start(Seconds, Socket).
handle_clean_sess_qos0(#mqtt_packet_connect{}, State) ->
@@ -522,11 +673,18 @@ handle_clean_sess_qos0(#mqtt_packet_connect{}, State) ->
handle_clean_sess_qos1(QoS0SessPresent, State) ->
handle_clean_sess(QoS0SessPresent, ?QOS_1, State).
-handle_clean_sess(_, QoS,
- State = #state{cfg = #cfg{clean_sess = true},
- auth_state = #auth_state{user = User,
- username = Username,
- authz_ctx = AuthzCtx}}) ->
+handle_clean_sess(
+ _,
+ QoS,
+ State = #state{
+ cfg = #cfg{clean_sess = true},
+ auth_state = #auth_state{
+ user = User,
+ username = Username,
+ authz_ctx = AuthzCtx
+ }
+ }
+) ->
%% "If the Server accepts a connection with CleanSession set to 1, the Server
%% MUST set Session Present to 0 in the CONNACK packet [MQTT-3.2.2-1].
SessPresent = false,
@@ -544,9 +702,16 @@ handle_clean_sess(_, QoS,
{error, ?CONNACK_NOT_AUTHORIZED}
end
end;
-handle_clean_sess(SessPresent, QoS,
- State0 = #state{cfg = #cfg{clean_sess = false,
- proto_ver = ProtoVer}}) ->
+handle_clean_sess(
+ SessPresent,
+ QoS,
+ State0 = #state{
+ cfg = #cfg{
+ clean_sess = false,
+ proto_ver = ProtoVer
+ }
+ }
+) ->
case get_queue(QoS, State0) of
{error, _} ->
%% Queue will be created later when client subscribes.
@@ -571,8 +736,8 @@ handle_clean_sess(SessPresent, QoS,
end.
-spec get_queue(qos(), state()) ->
- {ok, amqqueue:amqqueue()} |
- {error, not_found | {resource_locked, amqqueue:amqqueue()}}.
+ {ok, amqqueue:amqqueue()}
+ | {error, not_found | {resource_locked, amqqueue:amqqueue()}}.
get_queue(QoS, State) ->
QName = queue_name(QoS, State),
case rabbit_amqqueue:lookup(QName) of
@@ -592,8 +757,10 @@ get_queue(QoS, State) ->
queue_name(?QOS_1, #state{cfg = #cfg{queue_qos1 = #resource{kind = queue} = Name}}) ->
Name;
-queue_name(QoS, #state{auth_state = #auth_state{vhost = VHost},
- cfg = #cfg{client_id = ClientId}}) ->
+queue_name(QoS, #state{
+ auth_state = #auth_state{vhost = VHost},
+ cfg = #cfg{client_id = ClientId}
+}) ->
QNameBin = rabbit_mqtt_util:queue_name_bin(ClientId, QoS),
rabbit_misc:r(VHost, queue, QNameBin).
@@ -602,23 +769,26 @@ queue_name(QoS, #state{auth_state = #auth_state{vhost = VHost},
cache_subscriptions(_SessionPresent = _SubscriptionsPresent = true, State) ->
SubsQos0 = topic_names(?QOS_0, State),
SubsQos1 = topic_names(?QOS_1, State),
- Subs = maps:merge(maps:from_keys(SubsQos0, ?QOS_0),
- maps:from_keys(SubsQos1, ?QOS_1)),
+ Subs = maps:merge(
+ maps:from_keys(SubsQos0, ?QOS_0),
+ maps:from_keys(SubsQos1, ?QOS_1)
+ ),
{ok, State#state{subscriptions = Subs}};
cache_subscriptions(_, _) ->
ok.
topic_names(QoS, State = #state{cfg = #cfg{exchange = Exchange}}) ->
Bindings =
- rabbit_binding:list_for_source_and_destination(
- Exchange,
- queue_name(QoS, State),
- %% Querying table rabbit_route is catastrophic for CPU usage.
- %% Querying table rabbit_reverse_route is acceptable because
- %% the source exchange is always the same in the MQTT plugin whereas
- %% the destination queue is different for each MQTT client and
- %% rabbit_reverse_route is sorted by destination queue.
- _Reverse = true),
+ rabbit_binding:list_for_source_and_destination(
+ Exchange,
+ queue_name(QoS, State),
+ %% Querying table rabbit_route is catastrophic for CPU usage.
+ %% Querying table rabbit_reverse_route is acceptable because
+ %% the source exchange is always the same in the MQTT plugin whereas
+ %% the destination queue is different for each MQTT client and
+ %% rabbit_reverse_route is sorted by destination queue.
+ _Reverse = true
+ ),
lists:map(fun(B) -> amqp_to_mqtt(B#binding.key) end, Bindings).
%% "If a Server receives a SUBSCRIBE Packet containing a Topic Filter that is identical
@@ -628,21 +798,24 @@ topic_names(QoS, State = #state{cfg = #cfg{exchange = Exchange}}) ->
%% could be different." [MQTT-3.8.4-3].
maybe_replace_old_sub(TopicName, QoS, State = #state{subscriptions = Subs}) ->
case Subs of
- #{TopicName := OldQoS}
- when OldQoS =/= QoS ->
+ #{TopicName := OldQoS} when
+ OldQoS =/= QoS
+ ->
replace_old_sub(OldQoS, TopicName, State);
_ ->
{ok, State}
end.
-replace_old_sub(QoS, TopicName, State0)->
+replace_old_sub(QoS, TopicName, State0) ->
QName = queue_name(QoS, State0),
case unbind(QName, TopicName, State0) of
{ok, _Output, State} ->
{ok, State};
{error, Reason, _State} = Err ->
- ?LOG_ERROR("Failed to unbind ~s with topic '~s': ~p",
- [rabbit_misc:rs(QName), TopicName, Reason]),
+ ?LOG_ERROR(
+ "Failed to unbind ~s with topic '~s': ~p",
+ [rabbit_misc:rs(QName), TopicName, Reason]
+ ),
Err
end.
@@ -656,85 +829,124 @@ hand_off_to_retainer(RetainerPid, Topic0, Msg) ->
rabbit_mqtt_retainer:retain(RetainerPid, Topic1, Msg),
ok.
-maybe_send_retained_message(RPid, #mqtt_topic{name = Topic0, qos = SubscribeQos},
- State0 = #state{packet_id = PacketId0,
- cfg = #cfg{send_fun = SendFun}}) ->
+maybe_send_retained_message(
+ RPid,
+ #mqtt_topic{name = Topic0, qos = SubscribeQos},
+ State0 = #state{
+ packet_id = PacketId0,
+ cfg = #cfg{send_fun = SendFun}
+ }
+) ->
Topic1 = amqp_to_mqtt(Topic0),
case rabbit_mqtt_retainer:fetch(RPid, Topic1) of
undefined ->
State0;
Msg ->
Qos = effective_qos(Msg#mqtt_msg.qos, SubscribeQos),
- {PacketId, State} = case Qos of
- ?QOS_0 ->
- {undefined, State0};
- ?QOS_1 ->
- {PacketId0, State0#state{packet_id = increment_packet_id(PacketId0)}}
- end,
+ {PacketId, State} =
+ case Qos of
+ ?QOS_0 ->
+ {undefined, State0};
+ ?QOS_1 ->
+ {PacketId0, State0#state{packet_id = increment_packet_id(PacketId0)}}
+ end,
SendFun(
- #mqtt_packet{fixed = #mqtt_packet_fixed{
- type = ?PUBLISH,
- qos = Qos,
- dup = false,
- retain = Msg#mqtt_msg.retain
- }, variable = #mqtt_packet_publish{
- packet_id = PacketId,
- topic_name = Topic1
- },
- payload = Msg#mqtt_msg.payload},
- State),
+ #mqtt_packet{
+ fixed = #mqtt_packet_fixed{
+ type = ?PUBLISH,
+ qos = Qos,
+ dup = false,
+ retain = Msg#mqtt_msg.retain
+ },
+ variable = #mqtt_packet_publish{
+ packet_id = PacketId,
+ topic_name = Topic1
+ },
+ payload = Msg#mqtt_msg.payload
+ },
+ State
+ ),
State
end.
-make_will_msg(#mqtt_packet_connect{will_flag = false}) ->
+make_will_msg(#mqtt_packet_connect{will_flag = false}) ->
undefined;
-make_will_msg(#mqtt_packet_connect{will_retain = Retain,
- will_qos = Qos,
- will_topic = Topic,
- will_msg = Msg}) ->
- #mqtt_msg{retain = Retain,
- qos = Qos,
- topic = Topic,
- dup = false,
- payload = Msg}.
-
-process_login(_UserBin, _PassBin, ClientId,
- #state{cfg = #cfg{peer_addr = Addr},
- auth_state = #auth_state{username = Username,
- user = User,
- vhost = VHost
- }} = State)
- when Username =/= undefined, User =/= undefined, VHost =/= underfined ->
+make_will_msg(#mqtt_packet_connect{
+ will_retain = Retain,
+ will_qos = Qos,
+ will_topic = Topic,
+ will_msg = Msg
+}) ->
+ #mqtt_msg{
+ retain = Retain,
+ qos = Qos,
+ topic = Topic,
+ dup = false,
+ payload = Msg
+ }.
+
+process_login(
+ _UserBin,
+ _PassBin,
+ ClientId,
+ #state{
+ cfg = #cfg{peer_addr = Addr},
+ auth_state = #auth_state{
+ username = Username,
+ user = User,
+ vhost = VHost
+ }
+ } = State
+) when
+ Username =/= undefined, User =/= undefined, VHost =/= underfined
+->
rabbit_core_metrics:auth_attempt_failed(list_to_binary(inet:ntoa(Addr)), Username, mqtt),
?LOG_ERROR(
- "MQTT detected duplicate connect attempt for client ID '~ts', user '~ts', vhost '~ts'",
- [ClientId, Username, VHost]),
+ "MQTT detected duplicate connect attempt for client ID '~ts', user '~ts', vhost '~ts'",
+ [ClientId, Username, VHost]
+ ),
{error, ?CONNACK_ID_REJECTED, State};
-process_login(UserBin, PassBin, ClientId,
- #state{auth_state = undefined,
- cfg = #cfg{socket = Sock,
- ssl_login_name = SslLoginName,
- peer_addr = Addr
- }} = State0) ->
+process_login(
+ UserBin,
+ PassBin,
+ ClientId,
+ #state{
+ auth_state = undefined,
+ cfg = #cfg{
+ socket = Sock,
+ ssl_login_name = SslLoginName,
+ peer_addr = Addr
+ }
+ } = State0
+) ->
{ok, {_PeerHost, _PeerPort, _Host, Port}} = rabbit_net:socket_ends(Sock, inbound),
{VHostPickedUsing, {VHost, UsernameBin}} = get_vhost(UserBin, SslLoginName, Port),
- ?LOG_DEBUG("MQTT vhost picked using ~s",
- [human_readable_vhost_lookup_strategy(VHostPickedUsing)]),
+ ?LOG_DEBUG(
+ "MQTT vhost picked using ~s",
+ [human_readable_vhost_lookup_strategy(VHostPickedUsing)]
+ ),
RemoteIpAddressBin = list_to_binary(inet:ntoa(Addr)),
- Input = #{vhost => VHost,
- username_bin => UsernameBin,
- pass_bin => PassBin,
- client_id => ClientId},
- case rabbit_misc:pipeline(
- [fun check_vhost_exists/1,
- fun check_vhost_connection_limit/1,
- fun check_vhost_alive/1,
- fun check_user_login/2,
- fun check_user_connection_limit/1,
- fun check_vhost_access/2,
- fun check_user_loopback/2
- ],
- Input, State0) of
+ Input = #{
+ vhost => VHost,
+ username_bin => UsernameBin,
+ pass_bin => PassBin,
+ client_id => ClientId
+ },
+ case
+ rabbit_misc:pipeline(
+ [
+ fun check_vhost_exists/1,
+ fun check_vhost_connection_limit/1,
+ fun check_vhost_alive/1,
+ fun check_user_login/2,
+ fun check_user_connection_limit/1,
+ fun check_vhost_access/2,
+ fun check_user_loopback/2
+ ],
+ Input,
+ State0
+ )
+ of
{ok, _Output, State} ->
rabbit_core_metrics:auth_attempt_succeeded(RemoteIpAddressBin, UsernameBin, mqtt),
{ok, State};
@@ -743,134 +955,174 @@ process_login(UserBin, PassBin, ClientId,
Err
end.
-check_vhost_exists(#{vhost := VHost,
- username_bin := UsernameBin}) ->
+check_vhost_exists(#{
+ vhost := VHost,
+ username_bin := UsernameBin
+}) ->
case rabbit_vhost:exists(VHost) of
- true ->
+ true ->
ok;
false ->
- ?LOG_ERROR("MQTT login failed for user '~s': virtual host '~s' does not exist",
- [UsernameBin, VHost]),
+ ?LOG_ERROR(
+ "MQTT login failed for user '~s': virtual host '~s' does not exist",
+ [UsernameBin, VHost]
+ ),
{error, ?CONNACK_BAD_CREDENTIALS}
end.
-check_vhost_connection_limit(#{vhost := VHost,
- client_id := ClientId,
- username_bin := Username}) ->
+check_vhost_connection_limit(#{
+ vhost := VHost,
+ client_id := ClientId,
+ username_bin := Username
+}) ->
case rabbit_vhost_limit:is_over_connection_limit(VHost) of
false ->
ok;
{true, Limit} ->
?LOG_ERROR(
- "Failed to create MQTT connection because vhost connection limit is reached; "
- "vhost: '~s'; connection limit: ~p; user: '~s'; client ID '~s'",
- [VHost, Limit, Username, ClientId]),
+ "Failed to create MQTT connection because vhost connection limit is reached; "
+ "vhost: '~s'; connection limit: ~p; user: '~s'; client ID '~s'",
+ [VHost, Limit, Username, ClientId]
+ ),
{error, ?CONNACK_NOT_AUTHORIZED}
end.
-check_vhost_alive(#{vhost := VHost,
- client_id := ClientId,
- username_bin := UsernameBin}) ->
+check_vhost_alive(#{
+ vhost := VHost,
+ client_id := ClientId,
+ username_bin := UsernameBin
+}) ->
case rabbit_vhost_sup_sup:is_vhost_alive(VHost) of
- true ->
+ true ->
ok;
false ->
?LOG_ERROR(
- "Failed to create MQTT connection because vhost is down; "
- "vhost: ~s; user: ~s; client ID: ~s",
- [VHost, UsernameBin, ClientId]),
+ "Failed to create MQTT connection because vhost is down; "
+ "vhost: ~s; user: ~s; client ID: ~s",
+ [VHost, UsernameBin, ClientId]
+ ),
{error, ?CONNACK_NOT_AUTHORIZED}
end.
-check_user_login(#{vhost := VHost,
- username_bin := UsernameBin,
- pass_bin := PassBin,
- client_id := ClientId
- } = In, State) ->
- AuthProps = case PassBin of
- none ->
- %% SSL user name provided.
- %% Authenticating using username only.
- [];
- _ ->
- [{password, PassBin},
- {vhost, VHost},
- {client_id, ClientId}]
- end,
- case rabbit_access_control:check_user_login(
- UsernameBin, AuthProps) of
+check_user_login(
+ #{
+ vhost := VHost,
+ username_bin := UsernameBin,
+ pass_bin := PassBin,
+ client_id := ClientId
+ } = In,
+ State
+) ->
+ AuthProps =
+ case PassBin of
+ none ->
+ %% SSL user name provided.
+ %% Authenticating using username only.
+ [];
+ _ ->
+ [
+ {password, PassBin},
+ {vhost, VHost},
+ {client_id, ClientId}
+ ]
+ end,
+ case
+ rabbit_access_control:check_user_login(
+ UsernameBin, AuthProps
+ )
+ of
{ok, User = #user{username = Username}} ->
notify_auth_result(user_authentication_success, Username, State),
{ok, maps:put(user, User, In), State};
{refused, Username, Msg, Args} ->
?LOG_ERROR(
- "Error on MQTT connection ~p~n"
- "access refused for user '~s' in vhost '~s' "
- ++ Msg,
- [self(), Username, VHost] ++ Args),
+ "Error on MQTT connection ~p~n"
+ "access refused for user '~s' in vhost '~s' " ++
+ Msg,
+ [self(), Username, VHost] ++ Args
+ ),
notify_auth_result(user_authentication_failure, Username, State),
{error, ?CONNACK_BAD_CREDENTIALS}
end.
notify_auth_result(AuthResult, Username, #state{cfg = #cfg{conn_name = ConnName}}) ->
rabbit_event:notify(
- AuthResult,
- [{name, Username},
- {connection_name, ConnName},
- {connection_type, network}]).
-
-check_user_connection_limit(#{user := #user{username = Username},
- client_id := ClientId}) ->
+ AuthResult,
+ [
+ {name, Username},
+ {connection_name, ConnName},
+ {connection_type, network}
+ ]
+ ).
+
+check_user_connection_limit(#{
+ user := #user{username = Username},
+ client_id := ClientId
+}) ->
case rabbit_auth_backend_internal:is_over_connection_limit(Username) of
false ->
ok;
{true, Limit} ->
?LOG_ERROR(
- "Failed to create MQTT connection because user connection limit is reached; "
- "user: '~s'; connection limit: ~p; client ID '~s'",
- [Username, Limit, ClientId]),
+ "Failed to create MQTT connection because user connection limit is reached; "
+ "user: '~s'; connection limit: ~p; client ID '~s'",
+ [Username, Limit, ClientId]
+ ),
{error, ?CONNACK_NOT_AUTHORIZED}
end.
-
-check_vhost_access(#{vhost := VHost,
- client_id := ClientId,
- user := User = #user{username = Username}
- } = In,
- #state{cfg = #cfg{peer_addr = PeerAddr}} = State) ->
+check_vhost_access(
+ #{
+ vhost := VHost,
+ client_id := ClientId,
+ user := User = #user{username = Username}
+ } = In,
+ #state{cfg = #cfg{peer_addr = PeerAddr}} = State
+) ->
AuthzCtx = #{<<"client_id">> => ClientId},
- try rabbit_access_control:check_vhost_access(
- User,
- VHost,
- {ip, PeerAddr},
- AuthzCtx) of
+ try
+ rabbit_access_control:check_vhost_access(
+ User,
+ VHost,
+ {ip, PeerAddr},
+ AuthzCtx
+ )
+ of
ok ->
{ok, maps:put(authz_ctx, AuthzCtx, In), State}
- catch exit:#amqp_error{name = not_allowed} ->
- ?LOG_ERROR(
+ catch
+ exit:#amqp_error{name = not_allowed} ->
+ ?LOG_ERROR(
"Error on MQTT connection ~p~n"
"access refused for user '~s'",
- [self(), Username]),
- {error, ?CONNACK_NOT_AUTHORIZED}
+ [self(), Username]
+ ),
+ {error, ?CONNACK_NOT_AUTHORIZED}
end.
-check_user_loopback(#{vhost := VHost,
- username_bin := UsernameBin,
- user := User,
- authz_ctx := AuthzCtx
- },
- #state{cfg = #cfg{peer_addr = PeerAddr}} = State) ->
+check_user_loopback(
+ #{
+ vhost := VHost,
+ username_bin := UsernameBin,
+ user := User,
+ authz_ctx := AuthzCtx
+ },
+ #state{cfg = #cfg{peer_addr = PeerAddr}} = State
+) ->
case rabbit_access_control:check_user_loopback(UsernameBin, PeerAddr) of
ok ->
- AuthState = #auth_state{user = User,
- username = UsernameBin,
- vhost = VHost,
- authz_ctx = AuthzCtx},
+ AuthState = #auth_state{
+ user = User,
+ username = UsernameBin,
+ vhost = VHost,
+ authz_ctx = AuthzCtx
+ },
{ok, State#state{auth_state = AuthState}};
not_allowed ->
?LOG_WARNING(
- "MQTT login failed: user '~s' can only connect via localhost",
- [UsernameBin]),
+ "MQTT login failed: user '~s' can only connect via localhost",
+ [UsernameBin]
+ ),
{error, ?CONNACK_NOT_AUTHORIZED}
end.
@@ -883,12 +1135,12 @@ get_vhost(UserBin, SslLogin, Port) ->
get_vhost_no_ssl(UserBin, Port) ->
case vhost_in_username(UserBin) of
- true ->
+ true ->
{vhost_in_username_or_default, get_vhost_username(UserBin)};
false ->
PortVirtualHostMapping = rabbit_runtime_parameters:value_global(
- mqtt_port_to_vhost_mapping
- ),
+ mqtt_port_to_vhost_mapping
+ ),
case get_vhost_from_port_mapping(Port, PortVirtualHostMapping) of
undefined ->
{default_vhost, {rabbit_mqtt_util:env(vhost), UserBin}};
@@ -899,13 +1151,13 @@ get_vhost_no_ssl(UserBin, Port) ->
get_vhost_ssl(UserBin, SslLoginName, Port) ->
UserVirtualHostMapping = rabbit_runtime_parameters:value_global(
- mqtt_default_vhosts
- ),
+ mqtt_default_vhosts
+ ),
case get_vhost_from_user_mapping(SslLoginName, UserVirtualHostMapping) of
undefined ->
PortVirtualHostMapping = rabbit_runtime_parameters:value_global(
- mqtt_port_to_vhost_mapping
- ),
+ mqtt_port_to_vhost_mapping
+ ),
case get_vhost_from_port_mapping(Port, PortVirtualHostMapping) of
undefined ->
{vhost_in_username_or_default, get_vhost_username(UserBin)};
@@ -918,24 +1170,26 @@ get_vhost_ssl(UserBin, SslLoginName, Port) ->
vhost_in_username(UserBin) ->
case application:get_env(?APP_NAME, ignore_colons_in_username) of
- {ok, true} -> false;
+ {ok, true} ->
+ false;
_ ->
%% split at the last colon, disallowing colons in username
case re:split(UserBin, ":(?!.*?:)") of
- [_, _] -> true;
- [UserBin] -> false
+ [_, _] -> true;
+ [UserBin] -> false
end
end.
get_vhost_username(UserBin) ->
Default = {rabbit_mqtt_util:env(vhost), UserBin},
case application:get_env(?APP_NAME, ignore_colons_in_username) of
- {ok, true} -> Default;
+ {ok, true} ->
+ Default;
_ ->
%% split at the last colon, disallowing colons in username
case re:split(UserBin, ":(?!.*?:)") of
- [Vhost, UserName] -> {Vhost, UserName};
- [UserBin] -> Default
+ [Vhost, UserName] -> {Vhost, UserName};
+ [UserBin] -> Default
end
end.
@@ -954,12 +1208,13 @@ get_vhost_from_port_mapping(_Port, not_found) ->
undefined;
get_vhost_from_port_mapping(Port, Mapping) ->
M = rabbit_data_coercion:to_proplist(Mapping),
- Res = case rabbit_misc:pget(rabbit_data_coercion:to_binary(Port), M) of
- undefined ->
- undefined;
- VHost ->
- VHost
- end,
+ Res =
+ case rabbit_misc:pget(rabbit_data_coercion:to_binary(Port), M) of
+ undefined ->
+ undefined;
+ VHost ->
+ VHost
+ end,
Res.
human_readable_vhost_lookup_strategy(vhost_in_username_or_default) ->
@@ -972,13 +1227,14 @@ human_readable_vhost_lookup_strategy(default_vhost) ->
"plugin configuration or default".
creds(User, Pass, SSLLoginName) ->
- DefaultUser = rabbit_mqtt_util:env(default_user),
- DefaultPass = rabbit_mqtt_util:env(default_pass),
- {ok, Anon} = application:get_env(?APP_NAME, allow_anonymous),
+ DefaultUser = rabbit_mqtt_util:env(default_user),
+ DefaultPass = rabbit_mqtt_util:env(default_pass),
+ {ok, Anon} = application:get_env(?APP_NAME, allow_anonymous),
{ok, TLSAuth} = application:get_env(?APP_NAME, ssl_cert_login),
- HaveDefaultCreds = Anon =:= true andalso
- is_binary(DefaultUser) andalso
- is_binary(DefaultPass),
+ HaveDefaultCreds =
+ Anon =:= true andalso
+ is_binary(DefaultUser) andalso
+ is_binary(DefaultPass),
CredentialsProvided = User =/= undefined orelse Pass =/= undefined,
CorrectCredentials = is_binary(User) andalso is_binary(Pass),
@@ -986,15 +1242,15 @@ creds(User, Pass, SSLLoginName) ->
case {CredentialsProvided, CorrectCredentials, SSLLoginProvided, HaveDefaultCreds} of
%% Username and password take priority
- {true, true, _, _} -> {User, Pass};
+ {true, true, _, _} -> {User, Pass};
%% Either username or password is provided
- {true, false, _, _} -> {invalid_creds, {User, Pass}};
+ {true, false, _, _} -> {invalid_creds, {User, Pass}};
%% rabbitmq_mqtt.ssl_cert_login is true. SSL user name provided.
%% Authenticating using username only.
- {false, false, true, _} -> {SSLLoginName, none};
+ {false, false, true, _} -> {SSLLoginName, none};
%% Anonymous connection uses default credentials
{false, false, false, true} -> {DefaultUser, DefaultPass};
- _ -> nocreds
+ _ -> nocreds
end.
supported_sub_qos(?QOS_0) -> ?QOS_0;
@@ -1011,8 +1267,10 @@ ensure_queue(QoS, State = #state{auth_state = #auth_state{user = #user{username
{ok, Q};
{error, {resource_locked, Q}} ->
QName = amqqueue:get_name(Q),
- ?LOG_DEBUG("MQTT deleting exclusive ~s owned by ~p",
- [rabbit_misc:rs(QName), ?amqqueue_v2_field_exclusive_owner(Q)]),
+ ?LOG_DEBUG(
+ "MQTT deleting exclusive ~s owned by ~p",
+ [rabbit_misc:rs(QName), ?amqqueue_v2_field_exclusive_owner(Q)]
+ ),
delete_queue(QName, Username),
create_queue(QoS, State);
{error, not_found} ->
@@ -1020,14 +1278,18 @@ ensure_queue(QoS, State = #state{auth_state = #auth_state{user = #user{username
end.
create_queue(
- QoS, #state{cfg = #cfg{
- client_id = ClientId,
- clean_sess = CleanSess},
- auth_state = #auth_state{
- vhost = VHost,
- user = User = #user{username = Username},
- authz_ctx = AuthzCtx}
- }) ->
+ QoS, #state{
+ cfg = #cfg{
+ client_id = ClientId,
+ clean_sess = CleanSess
+ },
+ auth_state = #auth_state{
+ vhost = VHost,
+ user = User = #user{username = Username},
+ authz_ctx = AuthzCtx
+ }
+ }
+) ->
QNameBin = rabbit_mqtt_util:queue_name_bin(ClientId, QoS),
QName = rabbit_misc:r(VHost, queue, QNameBin),
%% configure access to queue required for queue.declare
@@ -1037,29 +1299,34 @@ create_queue(
false ->
rabbit_core_metrics:queue_declared(QName),
QArgs = queue_args(QoS, CleanSess),
- Q0 = amqqueue:new(QName,
- self(),
- _Durable = true,
- _AutoDelete = false,
- queue_owner(CleanSess),
- QArgs,
- VHost,
- #{user => Username},
- queue_type(QoS, CleanSess, QArgs)
- ),
+ Q0 = amqqueue:new(
+ QName,
+ self(),
+ _Durable = true,
+ _AutoDelete = false,
+ queue_owner(CleanSess),
+ QArgs,
+ VHost,
+ #{user => Username},
+ queue_type(QoS, CleanSess, QArgs)
+ ),
case rabbit_queue_type:declare(Q0, node()) of
{new, Q} when ?is_amqqueue(Q) ->
rabbit_core_metrics:queue_created(QName),
{ok, Q};
Other ->
- ?LOG_ERROR("Failed to declare ~s: ~p",
- [rabbit_misc:rs(QName), Other]),
+ ?LOG_ERROR(
+ "Failed to declare ~s: ~p",
+ [rabbit_misc:rs(QName), Other]
+ ),
{error, queue_declare}
end;
{true, Limit} ->
- ?LOG_ERROR("cannot declare ~s because "
- "queue limit ~p in vhost '~s' is reached",
- [rabbit_misc:rs(QName), Limit, VHost]),
+ ?LOG_ERROR(
+ "cannot declare ~s because "
+ "queue limit ~p in vhost '~s' is reached",
+ [rabbit_misc:rs(QName), Limit, VHost]
+ ),
{error, access_refused}
end;
{error, access_refused} = E ->
@@ -1076,12 +1343,13 @@ queue_owner(false) ->
none.
queue_args(QoS, false) ->
- Args = case rabbit_mqtt_util:env(subscription_ttl) of
- Ms when is_integer(Ms) ->
- [{<<"x-expires">>, long, Ms}];
- _ ->
- []
- end,
+ Args =
+ case rabbit_mqtt_util:env(subscription_ttl) of
+ Ms when is_integer(Ms) ->
+ [{<<"x-expires">>, long, Ms}];
+ _ ->
+ []
+ end,
case {QoS, rabbit_mqtt_util:env(durable_queue_type)} of
{?QOS_1, quorum} ->
[{<<"x-queue-type">>, longstr, <<"quorum">>} | Args];
@@ -1101,13 +1369,18 @@ queue_type(?QOS_0, true, QArgs) ->
queue_type(_, _, QArgs) ->
rabbit_amqqueue:get_queue_type(QArgs).
-consume(Q, QoS, #state{
- queue_states = QStates0,
- cfg = #cfg{prefetch = Prefetch},
- auth_state = #auth_state{
- authz_ctx = AuthzCtx,
- user = User = #user{username = Username}}
- } = State0) ->
+consume(
+ Q,
+ QoS,
+ #state{
+ queue_states = QStates0,
+ cfg = #cfg{prefetch = Prefetch},
+ auth_state = #auth_state{
+ authz_ctx = AuthzCtx,
+ user = User = #user{username = Username}
+ }
+ } = State0
+) ->
QName = amqqueue:get_name(Q),
%% read access to queue required for basic.consume
case check_resource_access(User, QName, read, AuthzCtx) of
@@ -1118,30 +1391,35 @@ consume(Q, QoS, #state{
%% explicitly calling rabbit_queue_type:consume/3.
{ok, State0};
_ ->
- Spec = #{no_ack => QoS =:= ?QOS_0,
- channel_pid => self(),
- limiter_pid => none,
- limiter_active => false,
- prefetch_count => Prefetch,
- consumer_tag => ?CONSUMER_TAG,
- exclusive_consume => false,
- args => [],
- ok_msg => undefined,
- acting_user => Username},
+ Spec = #{
+ no_ack => QoS =:= ?QOS_0,
+ channel_pid => self(),
+ limiter_pid => none,
+ limiter_active => false,
+ prefetch_count => Prefetch,
+ consumer_tag => ?CONSUMER_TAG,
+ exclusive_consume => false,
+ args => [],
+ ok_msg => undefined,
+ acting_user => Username
+ },
rabbit_amqqueue:with(
- QName,
- fun(Q1) ->
- case rabbit_queue_type:consume(Q1, Spec, QStates0) of
- {ok, QStates} ->
- State1 = State0#state{queue_states = QStates},
- State = maybe_set_queue_qos1(QoS, State1),
- {ok, State};
- {error, Reason} = Err ->
- ?LOG_ERROR("Failed to consume from ~s: ~p",
- [rabbit_misc:rs(QName), Reason]),
- Err
- end
- end)
+ QName,
+ fun(Q1) ->
+ case rabbit_queue_type:consume(Q1, Spec, QStates0) of
+ {ok, QStates} ->
+ State1 = State0#state{queue_states = QStates},
+ State = maybe_set_queue_qos1(QoS, State1),
+ {ok, State};
+ {error, Reason} = Err ->
+ ?LOG_ERROR(
+ "Failed to consume from ~s: ~p",
+ [rabbit_misc:rs(QName), Reason]
+ ),
+ Err
+ end
+ end
+ )
end;
{error, access_refused} = Err ->
Err
@@ -1163,25 +1441,37 @@ binding_action_with_checks(Input, State) ->
%% Same permission checks required for both binding and unbinding
%% queue to / from topic exchange.
rabbit_misc:pipeline(
- [fun check_queue_write_access/2,
- fun check_exchange_read_access/2,
- fun check_topic_access/2,
- fun binding_action/2],
- Input, State).
+ [
+ fun check_queue_write_access/2,
+ fun check_exchange_read_access/2,
+ fun check_topic_access/2,
+ fun binding_action/2
+ ],
+ Input,
+ State
+ ).
check_queue_write_access(
- {QueueName, _, _},
- #state{auth_state = #auth_state{
- user = User,
- authz_ctx = AuthzCtx}}) ->
+ {QueueName, _, _},
+ #state{
+ auth_state = #auth_state{
+ user = User,
+ authz_ctx = AuthzCtx
+ }
+ }
+) ->
%% write access to queue required for queue.(un)bind
check_resource_access(User, QueueName, write, AuthzCtx).
check_exchange_read_access(
- _, #state{cfg = #cfg{exchange = ExchangeName},
- auth_state = #auth_state{
- user = User,
- authz_ctx = AuthzCtx}}) ->
+ _, #state{
+ cfg = #cfg{exchange = ExchangeName},
+ auth_state = #auth_state{
+ user = User,
+ authz_ctx = AuthzCtx
+ }
+ }
+) ->
%% read access to exchange required for queue.(un)bind
check_resource_access(User, ExchangeName, read, AuthzCtx).
@@ -1189,57 +1479,72 @@ check_topic_access({_, TopicName, _}, State) ->
check_topic_access(TopicName, read, State).
binding_action(
- {QueueName, TopicName, BindingFun},
- #state{cfg = #cfg{exchange = ExchangeName},
- auth_state = #auth_state{user = #user{username = Username}}}) ->
+ {QueueName, TopicName, BindingFun},
+ #state{
+ cfg = #cfg{exchange = ExchangeName},
+ auth_state = #auth_state{user = #user{username = Username}}
+ }
+) ->
RoutingKey = mqtt_to_amqp(TopicName),
- Binding = #binding{source = ExchangeName,
- destination = QueueName,
- key = RoutingKey},
+ Binding = #binding{
+ source = ExchangeName,
+ destination = QueueName,
+ key = RoutingKey
+ },
BindingFun(Binding, Username).
publish_to_queues(
- #mqtt_msg{qos = Qos,
- topic = Topic,
- dup = Dup,
- packet_id = PacketId,
- payload = Payload},
- #state{cfg = #cfg{exchange = ExchangeName,
- delivery_flow = Flow,
- conn_name = ConnName,
- trace_state = TraceState},
- auth_state = #auth_state{username = Username}
- } = State) ->
+ #mqtt_msg{
+ qos = Qos,
+ topic = Topic,
+ dup = Dup,
+ packet_id = PacketId,
+ payload = Payload
+ },
+ #state{
+ cfg = #cfg{
+ exchange = ExchangeName,
+ delivery_flow = Flow,
+ conn_name = ConnName,
+ trace_state = TraceState
+ },
+ auth_state = #auth_state{username = Username}
+ } = State
+) ->
RoutingKey = mqtt_to_amqp(Topic),
Confirm = Qos > ?QOS_0,
- Headers = [{<<"x-mqtt-publish-qos">>, byte, Qos},
- {<<"x-mqtt-dup">>, bool, Dup}],
+ Headers = [
+ {<<"x-mqtt-publish-qos">>, byte, Qos},
+ {<<"x-mqtt-dup">>, bool, Dup}
+ ],
Props = #'P_basic'{
- headers = Headers,
- delivery_mode = delivery_mode(Qos)},
+ headers = Headers,
+ delivery_mode = delivery_mode(Qos)
+ },
{ClassId, _MethodId} = rabbit_framing_amqp_0_9_1:method_id('basic.publish'),
Content = #content{
- class_id = ClassId,
- properties = Props,
- properties_bin = none,
- protocol = none,
- payload_fragments_rev = [Payload]
- },
+ class_id = ClassId,
+ properties = Props,
+ properties_bin = none,
+ protocol = none,
+ payload_fragments_rev = [Payload]
+ },
BasicMessage = #basic_message{
- exchange_name = ExchangeName,
- routing_keys = [RoutingKey],
- content = Content,
- id = <<>>, %% GUID set in rabbit_classic_queue
- is_persistent = Confirm
- },
+ exchange_name = ExchangeName,
+ routing_keys = [RoutingKey],
+ content = Content,
+ %% GUID set in rabbit_classic_queue
+ id = <<>>,
+ is_persistent = Confirm
+ },
Delivery = #delivery{
- mandatory = false,
- confirm = Confirm,
- sender = self(),
- message = BasicMessage,
- msg_seq_no = PacketId,
- flow = Flow
- },
+ mandatory = false,
+ confirm = Confirm,
+ sender = self(),
+ message = BasicMessage,
+ msg_seq_no = PacketId,
+ flow = Flow
+ },
case rabbit_exchange:lookup(ExchangeName) of
{ok, Exchange} ->
QNames = rabbit_exchange:route(Exchange, Delivery),
@@ -1250,39 +1555,61 @@ publish_to_queues(
{error, exchange_not_found, State}
end.
-deliver_to_queues(Delivery,
- RoutedToQNames,
- State0 = #state{queue_states = QStates0,
- cfg = #cfg{proto_ver = ProtoVer}}) ->
+deliver_to_queues(
+ Delivery,
+ RoutedToQNames,
+ State0 = #state{
+ queue_states = QStates0,
+ cfg = #cfg{proto_ver = ProtoVer}
+ }
+) ->
Qs0 = rabbit_amqqueue:lookup(RoutedToQNames),
Qs = rabbit_amqqueue:prepend_extra_bcc(Qs0),
case rabbit_queue_type:deliver(Qs, Delivery, QStates0) of
{ok, QStates, Actions} ->
rabbit_global_counters:messages_routed(ProtoVer, length(Qs)),
- State = process_routing_confirm(Delivery, Qs,
- State0#state{queue_states = QStates}),
+ State = process_routing_confirm(
+ Delivery,
+ Qs,
+ State0#state{queue_states = QStates}
+ ),
%% Actions must be processed after registering confirms as actions may
%% contain rejections of publishes.
{ok, handle_queue_actions(Actions, State)};
{error, Reason} ->
- ?LOG_ERROR("Failed to deliver message with packet_id=~p to queues: ~p",
- [Delivery#delivery.msg_seq_no, Reason]),
+ ?LOG_ERROR(
+ "Failed to deliver message with packet_id=~p to queues: ~p",
+ [Delivery#delivery.msg_seq_no, Reason]
+ ),
{error, Reason, State0}
end.
-process_routing_confirm(#delivery{confirm = false},
- [], State = #state{cfg = #cfg{proto_ver = ProtoVer}}) ->
+process_routing_confirm(
+ #delivery{confirm = false},
+ [],
+ State = #state{cfg = #cfg{proto_ver = ProtoVer}}
+) ->
rabbit_global_counters:messages_unroutable_dropped(ProtoVer, 1),
State;
-process_routing_confirm(#delivery{confirm = true,
- msg_seq_no = undefined},
- [], State = #state{cfg = #cfg{proto_ver = ProtoVer}}) ->
+process_routing_confirm(
+ #delivery{
+ confirm = true,
+ msg_seq_no = undefined
+ },
+ [],
+ State = #state{cfg = #cfg{proto_ver = ProtoVer}}
+) ->
%% unroutable will message with QoS > 0
rabbit_global_counters:messages_unroutable_dropped(ProtoVer, 1),
State;
-process_routing_confirm(#delivery{confirm = true,
- msg_seq_no = PktId},
- [], State = #state{cfg = #cfg{proto_ver = ProtoVer}}) ->
+process_routing_confirm(
+ #delivery{
+ confirm = true,
+ msg_seq_no = PktId
+ },
+ [],
+ State = #state{cfg = #cfg{proto_ver = ProtoVer}}
+) ->
rabbit_global_counters:messages_unroutable_returned(ProtoVer, 1),
%% MQTT 5 spec:
%% If the Server knows that there are no matching subscribers, it MAY use
@@ -1291,42 +1618,75 @@ process_routing_confirm(#delivery{confirm = true,
State;
process_routing_confirm(#delivery{confirm = false}, _, State) ->
State;
-process_routing_confirm(#delivery{confirm = true,
- msg_seq_no = undefined}, [_|_], State) ->
+process_routing_confirm(
+ #delivery{
+ confirm = true,
+ msg_seq_no = undefined
+ },
+ [_ | _],
+ State
+) ->
%% routable will message with QoS > 0
State;
-process_routing_confirm(#delivery{confirm = true,
- msg_seq_no = PktId},
- Qs, State = #state{unacked_client_pubs = U0}) ->
+process_routing_confirm(
+ #delivery{
+ confirm = true,
+ msg_seq_no = PktId
+ },
+ Qs,
+ State = #state{unacked_client_pubs = U0}
+) ->
QNames = lists:map(fun amqqueue:get_name/1, Qs),
U = rabbit_mqtt_confirms:insert(PktId, QNames, U0),
State#state{unacked_client_pubs = U}.
-send_puback(PktIds0, State)
- when is_list(PktIds0) ->
+send_puback(PktIds0, State) when
+ is_list(PktIds0)
+->
%% Classic queues confirm messages unordered.
%% Let's sort them here assuming most MQTT clients send with an increasing packet identifier.
PktIds = lists:usort(PktIds0),
- lists:foreach(fun(Id) ->
- send_puback(Id, State)
- end, PktIds);
-send_puback(PktId, State = #state{cfg = #cfg{send_fun = SendFun,
- proto_ver = ProtoVer}}) ->
+ lists:foreach(
+ fun(Id) ->
+ send_puback(Id, State)
+ end,
+ PktIds
+ );
+send_puback(
+ PktId,
+ State = #state{
+ cfg = #cfg{
+ send_fun = SendFun,
+ proto_ver = ProtoVer
+ }
+ }
+) ->
rabbit_global_counters:messages_confirmed(ProtoVer, 1),
SendFun(
- #mqtt_packet{fixed = #mqtt_packet_fixed{type = ?PUBACK},
- variable = #mqtt_packet_publish{packet_id = PktId}},
- State).
-
-serialise_and_send_to_client(Packet, #state{cfg = #cfg{proto_ver = ProtoVer,
- socket = Sock}}) ->
+ #mqtt_packet{
+ fixed = #mqtt_packet_fixed{type = ?PUBACK},
+ variable = #mqtt_packet_publish{packet_id = PktId}
+ },
+ State
+ ).
+
+serialise_and_send_to_client(Packet, #state{
+ cfg = #cfg{
+ proto_ver = ProtoVer,
+ socket = Sock
+ }
+}) ->
Data = rabbit_mqtt_packet:serialise(Packet, ProtoVer),
- try rabbit_net:port_command(Sock, Data)
- catch error:Error ->
- ?LOG_ERROR("MQTT: a socket write failed: ~p", [Error]),
- ?LOG_DEBUG("MQTT failed to write to socket ~p, error: ~p, "
- "fixed packet header: ~p, variable packet header: ~p",
- [Sock, Error, Packet#mqtt_packet.fixed, Packet#mqtt_packet.variable])
+ try
+ rabbit_net:port_command(Sock, Data)
+ catch
+ error:Error ->
+ ?LOG_ERROR("MQTT: a socket write failed: ~p", [Error]),
+ ?LOG_DEBUG(
+ "MQTT failed to write to socket ~p, error: ~p, "
+ "fixed packet header: ~p, variable packet header: ~p",
+ [Sock, Error, Packet#mqtt_packet.fixed, Packet#mqtt_packet.variable]
+ )
end.
-spec serialise(#mqtt_packet{}, state()) ->
@@ -1338,11 +1698,13 @@ serialise(Packet, #state{cfg = #cfg{proto_ver = ProtoVer}}) ->
ok.
terminate(SendWill, ConnName, ProtoFamily, State) ->
maybe_send_will(SendWill, ConnName, State),
- Infos = [{name, ConnName},
- {node, node()},
- {pid, self()},
- {disconnected_at, os:system_time(milli_seconds)}
- ] ++ additional_connection_closed_info(ProtoFamily, State),
+ Infos =
+ [
+ {name, ConnName},
+ {node, node()},
+ {pid, self()},
+ {disconnected_at, os:system_time(milli_seconds)}
+ ] ++ additional_connection_closed_info(ProtoFamily, State),
rabbit_core_metrics:connection_closed(self()),
rabbit_event:notify(connection_closed, Infos),
rabbit_networking:unregister_non_amqp_connection(self()),
@@ -1352,13 +1714,23 @@ terminate(SendWill, ConnName, ProtoFamily, State) ->
maybe_delete_mqtt_qos0_queue(State).
maybe_send_will(
- true, ConnStr,
- #state{cfg = #cfg{retainer_pid = RPid,
- will_msg = WillMsg = #mqtt_msg{retain = Retain,
- topic = Topic}}
- } = State) ->
- ?LOG_DEBUG("sending MQTT will message to topic ~s on connection ~s",
- [Topic, ConnStr]),
+ true,
+ ConnStr,
+ #state{
+ cfg = #cfg{
+ retainer_pid = RPid,
+ will_msg =
+ WillMsg = #mqtt_msg{
+ retain = Retain,
+ topic = Topic
+ }
+ }
+ } = State
+) ->
+ ?LOG_DEBUG(
+ "sending MQTT will message to topic ~s on connection ~s",
+ [Topic, ConnStr]
+ ),
case check_topic_access(Topic, write, State) of
ok ->
_ = publish_to_queues(WillMsg, State),
@@ -1368,24 +1740,32 @@ maybe_send_will(
true ->
hand_off_to_retainer(RPid, Topic, WillMsg)
end;
- {error, access_refused = Reason} ->
+ {error, access_refused = Reason} ->
?LOG_ERROR("failed to send will message: ~p", [Reason])
end;
maybe_send_will(_, _, _) ->
ok.
additional_connection_closed_info(
- ProtoFamily,
- State = #state{auth_state = #auth_state{vhost = VHost,
- username = Username}}) ->
- [{protocol, {ProtoFamily, proto_version_tuple(State)}},
- {vhost, VHost},
- {user, Username}];
+ ProtoFamily,
+ State = #state{
+ auth_state = #auth_state{
+ vhost = VHost,
+ username = Username
+ }
+ }
+) ->
+ [
+ {protocol, {ProtoFamily, proto_version_tuple(State)}},
+ {vhost, VHost},
+ {user, Username}
+ ];
additional_connection_closed_info(_, _) ->
[].
-maybe_unregister_client(#state{cfg = #cfg{client_id = ClientId}})
- when ClientId =/= undefined ->
+maybe_unregister_client(#state{cfg = #cfg{client_id = ClientId}}) when
+ ClientId =/= undefined
+->
case rabbit_mqtt_ff:track_client_id_in_ra() of
true ->
%% ignore any errors as we are shutting down
@@ -1397,14 +1777,18 @@ maybe_unregister_client(_) ->
ok.
maybe_delete_mqtt_qos0_queue(
- State = #state{cfg = #cfg{clean_sess = true},
- auth_state = #auth_state{username = Username}}) ->
+ State = #state{
+ cfg = #cfg{clean_sess = true},
+ auth_state = #auth_state{username = Username}
+ }
+) ->
case get_queue(?QOS_0, State) of
{ok, Q} ->
%% double check we delete the right queue
case {amqqueue:get_type(Q), amqqueue:get_pid(Q)} of
- {?QUEUE_TYPE_QOS_0, Pid}
- when Pid =:= self() ->
+ {?QUEUE_TYPE_QOS_0, Pid} when
+ Pid =:= self()
+ ->
rabbit_queue_type:delete(Q, false, false, Username);
_ ->
ok
@@ -1417,19 +1801,21 @@ maybe_delete_mqtt_qos0_queue(_) ->
delete_queue(QName, Username) ->
rabbit_amqqueue:with(
- QName,
- fun (Q) ->
- rabbit_queue_type:delete(Q, false, false, Username)
- end,
- fun (not_found) ->
- ok;
- ({absent, Q, crashed}) ->
- rabbit_classic_queue:delete_crashed(Q, Username);
- ({absent, Q, stopped}) ->
- rabbit_classic_queue:delete_crashed(Q, Username);
- ({absent, _Q, _Reason}) ->
- ok
- end).
+ QName,
+ fun(Q) ->
+ rabbit_queue_type:delete(Q, false, false, Username)
+ end,
+ fun
+ (not_found) ->
+ ok;
+ ({absent, Q, crashed}) ->
+ rabbit_classic_queue:delete_crashed(Q, Username);
+ ({absent, Q, stopped}) ->
+ rabbit_classic_queue:delete_crashed(Q, Username);
+ ({absent, _Q, _Reason}) ->
+ ok
+ end
+ ).
-spec handle_pre_hibernate() -> ok.
handle_pre_hibernate() ->
@@ -1437,16 +1823,25 @@ handle_pre_hibernate() ->
erase(topic_permission_cache),
ok.
--spec handle_ra_event(register_timeout
-| {applied, [{reference(), ok}]}
-| {not_leader, term(), reference()}, state()) -> state().
-handle_ra_event({applied, [{Corr, ok}]},
- State = #state{register_state = {pending, Corr}}) ->
+-spec handle_ra_event(
+ register_timeout
+ | {applied, [{reference(), ok}]}
+ | {not_leader, term(), reference()},
+ state()
+) -> state().
+handle_ra_event(
+ {applied, [{Corr, ok}]},
+ State = #state{register_state = {pending, Corr}}
+) ->
%% success case - command was applied transition into registered state
State#state{register_state = registered};
-handle_ra_event({not_leader, Leader, Corr},
- State = #state{register_state = {pending, Corr},
- cfg = #cfg{client_id = ClientId}}) ->
+handle_ra_event(
+ {not_leader, Leader, Corr},
+ State = #state{
+ register_state = {pending, Corr},
+ cfg = #cfg{client_id = ClientId}
+ }
+) ->
case rabbit_mqtt_ff:track_client_id_in_ra() of
true ->
%% retry command against actual leader
@@ -1455,9 +1850,13 @@ handle_ra_event({not_leader, Leader, Corr},
false ->
State
end;
-handle_ra_event(register_timeout,
- State = #state{register_state = {pending, _Corr},
- cfg = #cfg{client_id = ClientId}}) ->
+handle_ra_event(
+ register_timeout,
+ State = #state{
+ register_state = {pending, _Corr},
+ cfg = #cfg{client_id = ClientId}
+ }
+) ->
case rabbit_mqtt_ff:track_client_id_in_ra() of
true ->
{ok, NewCorr} = rabbit_mqtt_collector:register(ClientId, self()),
@@ -1473,9 +1872,13 @@ handle_ra_event(Evt, State) ->
-spec handle_down(term(), state()) ->
{ok, state()} | {error, Reason :: any()}.
-handle_down({{'DOWN', QName}, _MRef, process, QPid, Reason},
- State0 = #state{queue_states = QStates0,
- unacked_client_pubs = U0}) ->
+handle_down(
+ {{'DOWN', QName}, _MRef, process, QPid, Reason},
+ State0 = #state{
+ queue_states = QStates0,
+ unacked_client_pubs = U0
+ }
+) ->
credit_flow:peer_down(QPid),
case rabbit_queue_type:handle_down(QPid, QName, Reason, QStates0) of
{ok, QStates1, Actions} ->
@@ -1483,33 +1886,44 @@ handle_down({{'DOWN', QName}, _MRef, process, QPid, Reason},
try handle_queue_actions(Actions, State1) of
State ->
{ok, State}
- catch throw:consuming_queue_down ->
- {error, consuming_queue_down}
+ catch
+ throw:consuming_queue_down ->
+ {error, consuming_queue_down}
end;
{eol, QStates1, QRef} ->
{ConfirmPktIds, U} = rabbit_mqtt_confirms:remove_queue(QRef, U0),
QStates = rabbit_queue_type:remove(QRef, QStates1),
- State = State0#state{queue_states = QStates,
- unacked_client_pubs = U},
+ State = State0#state{
+ queue_states = QStates,
+ unacked_client_pubs = U
+ },
send_puback(ConfirmPktIds, State),
{ok, State}
end.
-spec handle_queue_event(
- {queue_event, rabbit_amqqueue:name() | ?QUEUE_TYPE_QOS_0, term()}, state()) ->
+ {queue_event, rabbit_amqqueue:name() | ?QUEUE_TYPE_QOS_0, term()}, state()
+) ->
{ok, state()} | {error, Reason :: any(), state()}.
-handle_queue_event({queue_event, ?QUEUE_TYPE_QOS_0, Msg},
- State0 = #state{qos0_messages_dropped = N}) ->
- State = case drop_qos0_message(State0) of
- false ->
- deliver_one_to_client(Msg, false, State0);
- true ->
- State0#state{qos0_messages_dropped = N + 1}
- end,
+handle_queue_event(
+ {queue_event, ?QUEUE_TYPE_QOS_0, Msg},
+ State0 = #state{qos0_messages_dropped = N}
+) ->
+ State =
+ case drop_qos0_message(State0) of
+ false ->
+ deliver_one_to_client(Msg, false, State0);
+ true ->
+ State0#state{qos0_messages_dropped = N + 1}
+ end,
{ok, State};
-handle_queue_event({queue_event, QName, Evt},
- State0 = #state{queue_states = QStates0,
- unacked_client_pubs = U0}) ->
+handle_queue_event(
+ {queue_event, QName, Evt},
+ State0 = #state{
+ queue_states = QStates0,
+ unacked_client_pubs = U0
+ }
+) ->
case rabbit_queue_type:handle_event(QName, Evt, QStates0) of
{ok, QStates, Actions} ->
State1 = State0#state{queue_states = QStates},
@@ -1519,8 +1933,10 @@ handle_queue_event({queue_event, QName, Evt},
State1 = handle_queue_actions(Actions, State0),
{ConfirmPktIds, U} = rabbit_mqtt_confirms:remove_queue(QName, U0),
QStates = rabbit_queue_type:remove(QName, QStates0),
- State = State1#state{queue_states = QStates,
- unacked_client_pubs = U},
+ State = State1#state{
+ queue_states = QStates,
+ unacked_client_pubs = U
+ },
send_puback(ConfirmPktIds, State),
{ok, State};
{protocol_error, _Type, _Reason, _ReasonArgs} = Error ->
@@ -1529,30 +1945,37 @@ handle_queue_event({queue_event, QName, Evt},
handle_queue_actions(Actions, #state{} = State0) ->
lists:foldl(
- fun ({deliver, ?CONSUMER_TAG, Ack, Msgs}, S) ->
- deliver_to_client(Msgs, Ack, S);
- ({settled, QName, PktIds}, S = #state{unacked_client_pubs = U0}) ->
- {ConfirmPktIds, U} = rabbit_mqtt_confirms:confirm(PktIds, QName, U0),
- send_puback(ConfirmPktIds, S),
- S#state{unacked_client_pubs = U};
- ({rejected, _QName, PktIds}, S = #state{unacked_client_pubs = U0}) ->
- %% Negative acks are supported in MQTT v5 only.
- %% Therefore, in MQTT v3 and v4 we ignore rejected messages.
- U = lists:foldl(
+ fun
+ ({deliver, ?CONSUMER_TAG, Ack, Msgs}, S) ->
+ deliver_to_client(Msgs, Ack, S);
+ ({settled, QName, PktIds}, S = #state{unacked_client_pubs = U0}) ->
+ {ConfirmPktIds, U} = rabbit_mqtt_confirms:confirm(PktIds, QName, U0),
+ send_puback(ConfirmPktIds, S),
+ S#state{unacked_client_pubs = U};
+ ({rejected, _QName, PktIds}, S = #state{unacked_client_pubs = U0}) ->
+ %% Negative acks are supported in MQTT v5 only.
+ %% Therefore, in MQTT v3 and v4 we ignore rejected messages.
+ U = lists:foldl(
fun(PktId, Acc0) ->
- case rabbit_mqtt_confirms:reject(PktId, Acc0) of
- {ok, Acc} -> Acc;
- {error, not_found} -> Acc0
- end
- end, U0, PktIds),
- S#state{unacked_client_pubs = U};
- ({block, QName}, S = #state{queues_soft_limit_exceeded = QSLE}) ->
- S#state{queues_soft_limit_exceeded = sets:add_element(QName, QSLE)};
- ({unblock, QName}, S = #state{queues_soft_limit_exceeded = QSLE}) ->
- S#state{queues_soft_limit_exceeded = sets:del_element(QName, QSLE)};
- ({queue_down, QName}, S) ->
- handle_queue_down(QName, S)
- end, State0, Actions).
+ case rabbit_mqtt_confirms:reject(PktId, Acc0) of
+ {ok, Acc} -> Acc;
+ {error, not_found} -> Acc0
+ end
+ end,
+ U0,
+ PktIds
+ ),
+ S#state{unacked_client_pubs = U};
+ ({block, QName}, S = #state{queues_soft_limit_exceeded = QSLE}) ->
+ S#state{queues_soft_limit_exceeded = sets:add_element(QName, QSLE)};
+ ({unblock, QName}, S = #state{queues_soft_limit_exceeded = QSLE}) ->
+ S#state{queues_soft_limit_exceeded = sets:del_element(QName, QSLE)};
+ ({queue_down, QName}, S) ->
+ handle_queue_down(QName, S)
+ end,
+ State0,
+ Actions
+ ).
handle_queue_down(QName, State0 = #state{cfg = #cfg{client_id = ClientId}}) ->
%% Classic queue is down.
@@ -1568,8 +1991,10 @@ handle_queue_down(QName, State0 = #state{cfg = #cfg{client_id = ClientId}}) ->
{ok, State} ->
State;
{error, _Reason} ->
- ?LOG_INFO("Terminating MQTT connection because consuming ~s is down.",
- [rabbit_misc:rs(QName)]),
+ ?LOG_INFO(
+ "Terminating MQTT connection because consuming ~s is down.",
+ [rabbit_misc:rs(QName)]
+ ),
throw(consuming_queue_down)
end
end;
@@ -1578,26 +2003,37 @@ handle_queue_down(QName, State0 = #state{cfg = #cfg{client_id = ClientId}}) ->
end.
deliver_to_client(Msgs, Ack, State) ->
- lists:foldl(fun(Msg, S) ->
- deliver_one_to_client(Msg, Ack, S)
- end, State, Msgs).
-
-deliver_one_to_client(Msg = {QNameOrType, QPid, QMsgId, _Redelivered,
- #basic_message{content = #content{properties = #'P_basic'{headers = Headers}}}},
- AckRequired, State0) ->
- PublisherQoS = case rabbit_mqtt_util:table_lookup(Headers, <<"x-mqtt-publish-qos">>) of
- {byte, QoS0} ->
- QoS0;
- undefined ->
- %% non-MQTT publishes are assumed to be QoS 1 regardless of delivery_mode
- ?QOS_1
- end,
- SubscriberQoS = case AckRequired of
- true ->
- ?QOS_1;
- false ->
- ?QOS_0
- end,
+ lists:foldl(
+ fun(Msg, S) ->
+ deliver_one_to_client(Msg, Ack, S)
+ end,
+ State,
+ Msgs
+ ).
+
+deliver_one_to_client(
+ Msg =
+ {QNameOrType, QPid, QMsgId, _Redelivered, #basic_message{
+ content = #content{properties = #'P_basic'{headers = Headers}}
+ }},
+ AckRequired,
+ State0
+) ->
+ PublisherQoS =
+ case rabbit_mqtt_util:table_lookup(Headers, <<"x-mqtt-publish-qos">>) of
+ {byte, QoS0} ->
+ QoS0;
+ undefined ->
+ %% non-MQTT publishes are assumed to be QoS 1 regardless of delivery_mode
+ ?QOS_1
+ end,
+ SubscriberQoS =
+ case AckRequired of
+ true ->
+ ?QOS_1;
+ false ->
+ ?QOS_0
+ end,
QoS = effective_qos(PublisherQoS, SubscriberQoS),
State1 = maybe_publish_to_client(Msg, QoS, State0),
State = maybe_auto_ack(AckRequired, QoS, QNameOrType, QMsgId, State1),
@@ -1615,28 +2051,33 @@ maybe_publish_to_client({_, _, _, _Redelivered = true, _}, ?QOS_0, State) ->
%% Do not redeliver to MQTT subscriber who gets message at most once.
State;
maybe_publish_to_client(
- {QNameOrType, _QPid, QMsgId, Redelivered,
- #basic_message{
- routing_keys = [RoutingKey | _CcRoutes],
- content = #content{payload_fragments_rev = FragmentsRev}}} = Msg,
- QoS, State0 = #state{cfg = #cfg{send_fun = SendFun}}) ->
+ {QNameOrType, _QPid, QMsgId, Redelivered, #basic_message{
+ routing_keys = [RoutingKey | _CcRoutes],
+ content = #content{payload_fragments_rev = FragmentsRev}
+ }} = Msg,
+ QoS,
+ State0 = #state{cfg = #cfg{send_fun = SendFun}}
+) ->
{PacketId, State} = msg_id_to_packet_id(QMsgId, QoS, State0),
Packet =
- #mqtt_packet{
- fixed = #mqtt_packet_fixed{
- type = ?PUBLISH,
- qos = QoS,
- %% "The value of the DUP flag from an incoming PUBLISH packet is not
- %% propagated when the PUBLISH Packet is sent to subscribers by the Server.
- %% The DUP flag in the outgoing PUBLISH packet is set independently to the
- %% incoming PUBLISH packet, its value MUST be determined solely by whether
- %% the outgoing PUBLISH packet is a retransmission [MQTT-3.3.1-3]."
- %% Therefore, we do not consider header value <<"x-mqtt-dup">> here.
- dup = Redelivered},
- variable = #mqtt_packet_publish{
- packet_id = PacketId,
- topic_name = amqp_to_mqtt(RoutingKey)},
- payload = lists:reverse(FragmentsRev)},
+ #mqtt_packet{
+ fixed = #mqtt_packet_fixed{
+ type = ?PUBLISH,
+ qos = QoS,
+ %% "The value of the DUP flag from an incoming PUBLISH packet is not
+ %% propagated when the PUBLISH Packet is sent to subscribers by the Server.
+ %% The DUP flag in the outgoing PUBLISH packet is set independently to the
+ %% incoming PUBLISH packet, its value MUST be determined solely by whether
+ %% the outgoing PUBLISH packet is a retransmission [MQTT-3.3.1-3]."
+ %% Therefore, we do not consider header value <<"x-mqtt-dup">> here.
+ dup = Redelivered
+ },
+ variable = #mqtt_packet_publish{
+ packet_id = PacketId,
+ topic_name = amqp_to_mqtt(RoutingKey)
+ },
+ payload = lists:reverse(FragmentsRev)
+ },
SendFun(Packet, State),
trace_tap_out(Msg, State),
message_delivered(QNameOrType, Redelivered, QoS, State),
@@ -1645,21 +2086,37 @@ maybe_publish_to_client(
msg_id_to_packet_id(_, ?QOS_0, State) ->
%% "A PUBLISH packet MUST NOT contain a Packet Identifier if its QoS value is set to 0 [MQTT-2.2.1-2]."
{undefined, State};
-msg_id_to_packet_id(QMsgId, ?QOS_1, #state{packet_id = PktId,
- unacked_server_pubs = U} = State) ->
- {PktId, State#state{packet_id = increment_packet_id(PktId),
- unacked_server_pubs = maps:put(PktId, QMsgId, U)}}.
+msg_id_to_packet_id(
+ QMsgId,
+ ?QOS_1,
+ #state{
+ packet_id = PktId,
+ unacked_server_pubs = U
+ } = State
+) ->
+ {PktId, State#state{
+ packet_id = increment_packet_id(PktId),
+ unacked_server_pubs = maps:put(PktId, QMsgId, U)
+ }}.
-spec increment_packet_id(packet_id()) -> packet_id().
-increment_packet_id(Id)
- when Id >= 16#ffff ->
+increment_packet_id(Id) when
+ Id >= 16#ffff
+->
1;
increment_packet_id(Id) ->
Id + 1.
-maybe_auto_ack(_AckRequired = true, ?QOS_0, QName, QMsgId,
- State = #state{queue_states = QStates0}) ->
- {ok, QStates, Actions} = rabbit_queue_type:settle(QName, complete, ?CONSUMER_TAG, [QMsgId], QStates0),
+maybe_auto_ack(
+ _AckRequired = true,
+ ?QOS_0,
+ QName,
+ QMsgId,
+ State = #state{queue_states = QStates0}
+) ->
+ {ok, QStates, Actions} = rabbit_queue_type:settle(
+ QName, complete, ?CONSUMER_TAG, [QMsgId], QStates0
+ ),
handle_queue_actions(Actions, State#state{queue_states = QStates});
maybe_auto_ack(_, _, _, _, State) ->
State.
@@ -1674,13 +2131,21 @@ maybe_notify_sent(QName, QPid, #state{queue_states = QStates}) ->
ok
end.
-trace_tap_out(Msg = {#resource{}, _, _, _, _},
- #state{auth_state = #auth_state{username = Username},
- cfg = #cfg{conn_name = ConnName,
- trace_state = TraceState}}) ->
+trace_tap_out(
+ Msg = {#resource{}, _, _, _, _},
+ #state{
+ auth_state = #auth_state{username = Username},
+ cfg = #cfg{
+ conn_name = ConnName,
+ trace_state = TraceState
+ }
+ }
+) ->
rabbit_trace:tap_out(Msg, ConnName, Username, TraceState);
-trace_tap_out(Msg0 = {?QUEUE_TYPE_QOS_0, _, _, _, _},
- State = #state{cfg = #cfg{trace_state = TraceState}}) ->
+trace_tap_out(
+ Msg0 = {?QUEUE_TYPE_QOS_0, _, _, _, _},
+ State = #state{cfg = #cfg{trace_state = TraceState}}
+) ->
case rabbit_trace:enabled(TraceState) of
false ->
ok;
@@ -1692,11 +2157,16 @@ trace_tap_out(Msg0 = {?QUEUE_TYPE_QOS_0, _, _, _, _},
end.
publish_to_queues_with_checks(
- TopicName, PublishFun,
- #state{cfg = #cfg{exchange = Exchange},
- auth_state = #auth_state{user = User,
- authz_ctx = AuthzCtx}
- } = State) ->
+ TopicName,
+ PublishFun,
+ #state{
+ cfg = #cfg{exchange = Exchange},
+ auth_state = #auth_state{
+ user = User,
+ authz_ctx = AuthzCtx
+ }
+ } = State
+) ->
case check_resource_access(User, Exchange, write, AuthzCtx) of
ok ->
case check_topic_access(TopicName, write, State) of
@@ -1711,57 +2181,75 @@ publish_to_queues_with_checks(
check_resource_access(User, Resource, Perm, Context) ->
V = {Resource, Context, Perm},
- Cache = case get(permission_cache) of
- undefined -> [];
- Other -> Other
- end,
+ Cache =
+ case get(permission_cache) of
+ undefined -> [];
+ Other -> Other
+ end,
case lists:member(V, Cache) of
true ->
ok;
false ->
try rabbit_access_control:check_resource_access(User, Resource, Perm, Context) of
ok ->
- CacheTail = lists:sublist(Cache, ?MAX_PERMISSION_CACHE_SIZE-1),
+ CacheTail = lists:sublist(Cache, ?MAX_PERMISSION_CACHE_SIZE - 1),
put(permission_cache, [V | CacheTail]),
ok
catch
- exit:#amqp_error{name = access_refused,
- explanation = Msg} ->
+ exit:#amqp_error{
+ name = access_refused,
+ explanation = Msg
+ } ->
?LOG_ERROR("MQTT resource access refused: ~s", [Msg]),
{error, access_refused}
end
end.
check_topic_access(
- TopicName, Access,
- #state{auth_state = #auth_state{user = User = #user{username = Username},
- vhost = VHost,
- authz_ctx = AuthzCtx},
- cfg = #cfg{client_id = ClientId,
- exchange = #resource{name = ExchangeBin}}}) ->
- Cache = case get(topic_permission_cache) of
- undefined -> [];
- Other -> Other
- end,
+ TopicName,
+ Access,
+ #state{
+ auth_state = #auth_state{
+ user = User = #user{username = Username},
+ vhost = VHost,
+ authz_ctx = AuthzCtx
+ },
+ cfg = #cfg{
+ client_id = ClientId,
+ exchange = #resource{name = ExchangeBin}
+ }
+ }
+) ->
+ Cache =
+ case get(topic_permission_cache) of
+ undefined -> [];
+ Other -> Other
+ end,
Key = {TopicName, Username, ClientId, VHost, ExchangeBin, Access},
case lists:member(Key, Cache) of
true ->
ok;
false ->
- Resource = #resource{virtual_host = VHost,
- kind = topic,
- name = ExchangeBin},
+ Resource = #resource{
+ virtual_host = VHost,
+ kind = topic,
+ name = ExchangeBin
+ },
RoutingKey = mqtt_to_amqp(TopicName),
- Context = #{routing_key => RoutingKey,
- variable_map => AuthzCtx},
+ Context = #{
+ routing_key => RoutingKey,
+ variable_map => AuthzCtx
+ },
try rabbit_access_control:check_topic_access(User, Resource, Access, Context) of
ok ->
CacheTail = lists:sublist(Cache, ?MAX_PERMISSION_CACHE_SIZE - 1),
put(topic_permission_cache, [Key | CacheTail]),
ok
catch
- exit:#amqp_error{name = access_refused,
- explanation = Msg} ->
+ exit:#amqp_error{
+ name = access_refused,
+ explanation = Msg
+ } ->
?LOG_ERROR("MQTT topic access refused: ~s", [Msg]),
{error, access_refused}
end
@@ -1771,7 +2259,7 @@ check_topic_access(
boolean().
drop_qos0_message(State) ->
mailbox_soft_limit_exceeded() andalso
- is_socket_busy(State#state.cfg#cfg.socket).
+ is_socket_busy(State#state.cfg#cfg.socket).
-spec mailbox_soft_limit_exceeded() ->
boolean().
@@ -1790,19 +2278,22 @@ mailbox_soft_limit_exceeded() ->
is_socket_busy(Socket) ->
case rabbit_net:getstat(Socket, [send_pend]) of
- {ok, [{send_pend, NumBytes}]}
- when is_number(NumBytes) andalso NumBytes > 0 ->
+ {ok, [{send_pend, NumBytes}]} when
+ is_number(NumBytes) andalso NumBytes > 0
+ ->
true;
_ ->
false
end.
-spec throttle(boolean(), boolean(), state()) -> boolean().
-throttle(Conserve, Connected, #state{queues_soft_limit_exceeded = QSLE,
- cfg = #cfg{published = Published}}) ->
+throttle(Conserve, Connected, #state{
+ queues_soft_limit_exceeded = QSLE,
+ cfg = #cfg{published = Published}
+}) ->
Conserve andalso (Published orelse not Connected) orelse
- not sets:is_empty(QSLE) orelse
- credit_flow:blocked().
+ not sets:is_empty(QSLE) orelse
+ credit_flow:blocked().
-spec info(rabbit_types:info_key(), state()) -> any().
info(host, #state{cfg = #cfg{host = Val}}) -> Val;
@@ -1812,23 +2303,19 @@ info(peer_port, #state{cfg = #cfg{peer_port = Val}}) -> Val;
info(connected_at, #state{cfg = #cfg{connected_at = Val}}) -> Val;
info(ssl_login_name, #state{cfg = #cfg{ssl_login_name = Val}}) -> Val;
info(vhost, #state{auth_state = #auth_state{vhost = Val}}) -> Val;
-info(user_who_performed_action, S) ->
- info(user, S);
+info(user_who_performed_action, S) -> info(user, S);
info(user, #state{auth_state = #auth_state{username = Val}}) -> Val;
info(clean_sess, #state{cfg = #cfg{clean_sess = Val}}) -> Val;
info(will_msg, #state{cfg = #cfg{will_msg = Val}}) -> Val;
info(retainer_pid, #state{cfg = #cfg{retainer_pid = Val}}) -> Val;
info(exchange, #state{cfg = #cfg{exchange = #resource{name = Val}}}) -> Val;
info(prefetch, #state{cfg = #cfg{prefetch = Val}}) -> Val;
-info(messages_unconfirmed, #state{unacked_client_pubs = Val}) ->
- rabbit_mqtt_confirms:size(Val);
-info(messages_unacknowledged, #state{unacked_server_pubs = Val}) ->
- maps:size(Val);
+info(messages_unconfirmed, #state{unacked_client_pubs = Val}) -> rabbit_mqtt_confirms:size(Val);
+info(messages_unacknowledged, #state{unacked_server_pubs = Val}) -> maps:size(Val);
info(node, _) -> node();
info(client_id, #state{cfg = #cfg{client_id = Val}}) -> Val;
%% for rabbitmq_management/priv/www/js/tmpl/connection.ejs
-info(client_properties, #state{cfg = #cfg{client_id = Val}}) ->
- [{client_id, longstr, Val}];
+info(client_properties, #state{cfg = #cfg{client_id = Val}}) -> [{client_id, longstr, Val}];
info(channel_max, _) -> 0;
%% Maximum packet size supported only in MQTT 5.0.
info(frame_max, _) -> 0;
@@ -1840,13 +2327,16 @@ info(Other, _) -> throw({bad_argument, Other}).
none | binary().
ssl_login_name(Sock) ->
case rabbit_net:peercert(Sock) of
- {ok, C} -> case rabbit_ssl:peer_cert_auth_name(C) of
- unsafe -> none;
- not_found -> none;
- Name -> Name
- end;
- {error, no_peercert} -> none;
- nossl -> none
+ {ok, C} ->
+ case rabbit_ssl:peer_cert_auth_name(C) of
+ unsafe -> none;
+ not_found -> none;
+ Name -> Name
+ end;
+ {error, no_peercert} ->
+ none;
+ nossl ->
+ none
end.
proto_integer_to_atom(3) ->
@@ -1860,47 +2350,73 @@ proto_version_tuple(#state{cfg = #cfg{proto_ver = ?MQTT_PROTO_V3}}) ->
proto_version_tuple(#state{cfg = #cfg{proto_ver = ?MQTT_PROTO_V4}}) ->
{3, 1, 1}.
-maybe_increment_publisher(State = #state{cfg = Cfg = #cfg{published = false,
- proto_ver = ProtoVer}}) ->
+maybe_increment_publisher(
+ State = #state{
+ cfg =
+ Cfg = #cfg{
+ published = false,
+ proto_ver = ProtoVer
+ }
+ }
+) ->
rabbit_global_counters:publisher_created(ProtoVer),
State#state{cfg = Cfg#cfg{published = true}};
maybe_increment_publisher(State) ->
State.
-maybe_decrement_publisher(#state{cfg = #cfg{published = true,
- proto_ver = ProtoVer}}) ->
+maybe_decrement_publisher(#state{
+ cfg = #cfg{
+ published = true,
+ proto_ver = ProtoVer
+ }
+}) ->
rabbit_global_counters:publisher_deleted(ProtoVer);
maybe_decrement_publisher(_) ->
ok.
%% Multiple subscriptions from the same connection count as one consumer.
-maybe_increment_consumer(#state{subscriptions = OldSubs},
- #state{subscriptions = NewSubs,
- cfg = #cfg{proto_ver = ProtoVer}})
- when map_size(OldSubs) =:= 0 andalso
- map_size(NewSubs) > 0 ->
+maybe_increment_consumer(
+ #state{subscriptions = OldSubs},
+ #state{
+ subscriptions = NewSubs,
+ cfg = #cfg{proto_ver = ProtoVer}
+ }
+) when
+ map_size(OldSubs) =:= 0 andalso
+ map_size(NewSubs) > 0
+->
rabbit_global_counters:consumer_created(ProtoVer);
maybe_increment_consumer(_, _) ->
ok.
-maybe_decrement_consumer(#state{subscriptions = Subs,
- cfg = #cfg{proto_ver = ProtoVer}})
- when map_size(Subs) > 0 ->
+maybe_decrement_consumer(#state{
+ subscriptions = Subs,
+ cfg = #cfg{proto_ver = ProtoVer}
+}) when
+ map_size(Subs) > 0
+->
rabbit_global_counters:consumer_deleted(ProtoVer);
maybe_decrement_consumer(_) ->
ok.
-maybe_decrement_consumer(#state{subscriptions = OldSubs},
- #state{subscriptions = NewSubs,
- cfg = #cfg{proto_ver = ProtoVer}})
- when map_size(OldSubs) > 0 andalso
- map_size(NewSubs) =:= 0 ->
+maybe_decrement_consumer(
+ #state{subscriptions = OldSubs},
+ #state{
+ subscriptions = NewSubs,
+ cfg = #cfg{proto_ver = ProtoVer}
+ }
+) when
+ map_size(OldSubs) > 0 andalso
+ map_size(NewSubs) =:= 0
+->
rabbit_global_counters:consumer_deleted(ProtoVer);
maybe_decrement_consumer(_, _) ->
ok.
-message_acknowledged(QName, #state{queue_states = QStates,
- cfg = #cfg{proto_ver = ProtoVer}}) ->
+message_acknowledged(QName, #state{
+ queue_states = QStates,
+ cfg = #cfg{proto_ver = ProtoVer}
+}) ->
case rabbit_queue_type:module(QName, QStates) of
{ok, QType} ->
rabbit_global_counters:messages_acknowledged(ProtoVer, QType, 1);
@@ -1908,17 +2424,27 @@ message_acknowledged(QName, #state{queue_states = QStates,
ok
end.
-message_delivered(?QUEUE_TYPE_QOS_0, false, ?QOS_0,
- #state{cfg = #cfg{proto_ver = ProtoVer}}) ->
+message_delivered(
+ ?QUEUE_TYPE_QOS_0,
+ false,
+ ?QOS_0,
+ #state{cfg = #cfg{proto_ver = ProtoVer}}
+) ->
rabbit_global_counters:messages_delivered(ProtoVer, ?QUEUE_TYPE_QOS_0, 1),
%% Technically, the message is not acked to a queue at all.
%% However, from a user perspective it is still auto acked because:
%% "In automatic acknowledgement mode, a message is considered to be successfully
%% delivered immediately after it is sent."
rabbit_global_counters:messages_delivered_consume_auto_ack(ProtoVer, ?QUEUE_TYPE_QOS_0, 1);
-message_delivered(QName, Redelivered, QoS,
- #state{queue_states = QStates,
- cfg = #cfg{proto_ver = ProtoVer}}) ->
+message_delivered(
+ QName,
+ Redelivered,
+ QoS,
+ #state{
+ queue_states = QStates,
+ cfg = #cfg{proto_ver = ProtoVer}
+ }
+) ->
case rabbit_queue_type:module(QName, QStates) of
{ok, QType} ->
rabbit_global_counters:messages_delivered(ProtoVer, QType, 1),
@@ -1940,65 +2466,72 @@ message_redelivered(_, _, _) ->
-spec format_status(state()) -> map().
format_status(
- #state{queue_states = QState,
- unacked_client_pubs = UnackClientPubs,
- unacked_server_pubs = UnackSerPubs,
- packet_id = PackID,
- subscriptions = Subscriptions,
- auth_state = AuthState,
- register_state = RegisterState,
- queues_soft_limit_exceeded = QSLE,
- qos0_messages_dropped = Qos0MsgsDropped,
- cfg = #cfg{
- socket = Socket,
- proto_ver = ProtoVersion,
- clean_sess = CleanSess,
- will_msg = WillMsg,
- exchange = Exchange,
- queue_qos1 = _,
- published = Published,
- ssl_login_name = SSLLoginName,
- retainer_pid = RetainerPid,
- delivery_flow = DeliveryFlow,
- trace_state = TraceState,
- prefetch = Prefetch,
- client_id = ClientID,
- conn_name = ConnName,
- peer_addr = PeerAddr,
- host = Host,
- port = Port,
- peer_host = PeerHost,
- peer_port = PeerPort,
- connected_at = ConnectedAt,
- send_fun = _
- }}) ->
- Cfg = #{socket => Socket,
- proto_ver => ProtoVersion,
- clean_sess => CleanSess,
- will_msg_defined => WillMsg =/= undefined,
- exchange => Exchange,
- published => Published,
- ssl_login_name => SSLLoginName,
- retainer_pid => RetainerPid,
-
- delivery_flow => DeliveryFlow,
- trace_state => TraceState,
- prefetch => Prefetch,
- client_id => ClientID,
- conn_name => ConnName,
- peer_addr => PeerAddr,
- host => Host,
- port => Port,
- peer_host => PeerHost,
- peer_port => PeerPort,
- connected_at => ConnectedAt},
- #{cfg => Cfg,
- queue_states => rabbit_queue_type:format_status(QState),
- unacked_client_pubs => UnackClientPubs,
- unacked_server_pubs => UnackSerPubs,
- packet_id => PackID,
- subscriptions => Subscriptions,
- auth_state => AuthState,
- register_state => RegisterState,
- queues_soft_limit_exceeded => QSLE,
- qos0_messages_dropped => Qos0MsgsDropped}.
+ #state{
+ queue_states = QState,
+ unacked_client_pubs = UnackClientPubs,
+ unacked_server_pubs = UnackSerPubs,
+ packet_id = PackID,
+ subscriptions = Subscriptions,
+ auth_state = AuthState,
+ register_state = RegisterState,
+ queues_soft_limit_exceeded = QSLE,
+ qos0_messages_dropped = Qos0MsgsDropped,
+ cfg = #cfg{
+ socket = Socket,
+ proto_ver = ProtoVersion,
+ clean_sess = CleanSess,
+ will_msg = WillMsg,
+ exchange = Exchange,
+ queue_qos1 = _,
+ published = Published,
+ ssl_login_name = SSLLoginName,
+ retainer_pid = RetainerPid,
+ delivery_flow = DeliveryFlow,
+ trace_state = TraceState,
+ prefetch = Prefetch,
+ client_id = ClientID,
+ conn_name = ConnName,
+ peer_addr = PeerAddr,
+ host = Host,
+ port = Port,
+ peer_host = PeerHost,
+ peer_port = PeerPort,
+ connected_at = ConnectedAt,
+ send_fun = _
+ }
+ }
+) ->
+ Cfg = #{
+ socket => Socket,
+ proto_ver => ProtoVersion,
+ clean_sess => CleanSess,
+ will_msg_defined => WillMsg =/= undefined,
+ exchange => Exchange,
+ published => Published,
+ ssl_login_name => SSLLoginName,
+ retainer_pid => RetainerPid,
+
+ delivery_flow => DeliveryFlow,
+ trace_state => TraceState,
+ prefetch => Prefetch,
+ client_id => ClientID,
+ conn_name => ConnName,
+ peer_addr => PeerAddr,
+ host => Host,
+ port => Port,
+ peer_host => PeerHost,
+ peer_port => PeerPort,
+ connected_at => ConnectedAt
+ },
+ #{
+ cfg => Cfg,
+ queue_states => rabbit_queue_type:format_status(QState),
+ unacked_client_pubs => UnackClientPubs,
+ unacked_server_pubs => UnackSerPubs,
+ packet_id => PackID,
+ subscriptions => Subscriptions,
+ auth_state => AuthState,
+ register_state => RegisterState,
+ queues_soft_limit_exceeded => QSLE,
+ qos0_messages_dropped => Qos0MsgsDropped
+ }.
diff --git a/deps/rabbitmq_mqtt/src/rabbit_mqtt_qos0_queue.erl b/deps/rabbitmq_mqtt/src/rabbit_mqtt_qos0_queue.erl
index deb0eff622..1244c96483 100644
--- a/deps/rabbitmq_mqtt/src/rabbit_mqtt_qos0_queue.erl
+++ b/deps/rabbitmq_mqtt/src/rabbit_mqtt_qos0_queue.erl
@@ -24,42 +24,50 @@
%% Stateless rabbit_queue_type callbacks.
-export([
- is_stateful/0,
- declare/2,
- delete/4,
- deliver/2,
- is_enabled/0,
- is_compatible/3,
- is_recoverable/1,
- recover/2,
- purge/1,
- policy_changed/1,
- info/2,
- stat/1,
- capabilities/0,
- notify_decorators/1
- ]).
+ is_stateful/0,
+ declare/2,
+ delete/4,
+ deliver/2,
+ is_enabled/0,
+ is_compatible/3,
+ is_recoverable/1,
+ recover/2,
+ purge/1,
+ policy_changed/1,
+ info/2,
+ stat/1,
+ capabilities/0,
+ notify_decorators/1
+]).
%% Stateful rabbit_queue_type callbacks are unsupported by this queue type.
--define(STATEFUL_CALLBACKS,
- [
- init/1,
- close/1,
- update/2,
- consume/3,
- cancel/5,
- handle_event/3,
- settle/5,
- credit/5,
- dequeue/5,
- state_info/1
- ]).
+-define(STATEFUL_CALLBACKS, [
+ init/1,
+ close/1,
+ update/2,
+ consume/3,
+ cancel/5,
+ handle_event/3,
+ settle/5,
+ credit/5,
+ dequeue/5,
+ state_info/1
+]).
-export(?STATEFUL_CALLBACKS).
-dialyzer({nowarn_function, ?STATEFUL_CALLBACKS}).
-define(UNSUPPORTED(Args), erlang:error(unsupported, Args)).
--define(INFO_KEYS, [type, name, durable, auto_delete, arguments,
- pid, owner_pid, state, messages]).
+-define(INFO_KEYS, [
+ type,
+ name,
+ durable,
+ auto_delete,
+ arguments,
+ pid,
+ owner_pid,
+ state,
+ messages
+]).
-spec is_stateful() ->
boolean().
@@ -67,8 +75,8 @@ is_stateful() ->
false.
-spec declare(amqqueue:amqqueue(), node()) ->
- {'new' | 'existing' | 'owner_died', amqqueue:amqqueue()} |
- {'absent', amqqueue:amqqueue(), rabbit_amqqueue:absent_reason()}.
+ {'new' | 'existing' | 'owner_died', amqqueue:amqqueue()}
+ | {'absent', amqqueue:amqqueue(), rabbit_amqqueue:absent_reason()}.
declare(Q0, _Node) ->
%% The queue gets persisted such that routing to this
%% queue (via the topic exchange) works as usual.
@@ -76,23 +84,29 @@ declare(Q0, _Node) ->
{created, Q} ->
Opts = amqqueue:get_options(Q),
ActingUser = maps:get(user, Opts, ?UNKNOWN_USER),
- rabbit_event:notify(queue_created,
- [{name, amqqueue:get_name(Q0)},
- {durable, true},
- {auto_delete, false},
- {exclusive, true},
- {type, amqqueue:get_type(Q0)},
- {arguments, amqqueue:get_arguments(Q0)},
- {user_who_performed_action, ActingUser}]),
+ rabbit_event:notify(
+ queue_created,
+ [
+ {name, amqqueue:get_name(Q0)},
+ {durable, true},
+ {auto_delete, false},
+ {exclusive, true},
+ {type, amqqueue:get_type(Q0)},
+ {arguments, amqqueue:get_arguments(Q0)},
+ {user_who_performed_action, ActingUser}
+ ]
+ ),
{new, Q};
Other ->
Other
end.
--spec delete(amqqueue:amqqueue(),
- boolean(),
- boolean(),
- rabbit_types:username()) ->
+-spec delete(
+ amqqueue:amqqueue(),
+ boolean(),
+ boolean(),
+ rabbit_types:username()
+) ->
rabbit_types:ok(non_neg_integer()).
delete(Q, _IfUnused, _IfEmpty, ActingUser) ->
QName = amqqueue:get_name(Q),
@@ -102,35 +116,41 @@ delete(Q, _IfUnused, _IfEmpty, ActingUser) ->
-spec deliver([{amqqueue:amqqueue(), stateless}], Delivery :: term()) ->
{[], rabbit_queue_type:actions()}.
-deliver(Qs, #delivery{message = BasicMessage,
- confirm = Confirm,
- msg_seq_no = SeqNo}) ->
- Msg = {queue_event, ?MODULE,
- {?MODULE, _QPid = none, _QMsgId = none, _Redelivered = false, BasicMessage}},
+deliver(Qs, #delivery{
+ message = BasicMessage,
+ confirm = Confirm,
+ msg_seq_no = SeqNo
+}) ->
+ Msg =
+ {queue_event, ?MODULE,
+ {?MODULE, _QPid = none, _QMsgId = none, _Redelivered = false, BasicMessage}},
{Pids, Actions} =
- case Confirm of
- false ->
- Pids0 = lists:map(fun({Q, stateless}) -> amqqueue:get_pid(Q) end, Qs),
- {Pids0, []};
- true ->
- %% We confirm the message directly here in the queue client.
- %% Alternatively, we could have the target MQTT connection process confirm the message.
- %% However, given that this message might be lost anyway between target MQTT connection
- %% process and MQTT subscriber, and we know that the MQTT subscriber wants to receive
- %% this message at most once, we confirm here directly.
- %% Benefits:
- %% 1. We do not block sending the confirmation back to the publishing client just because a single
- %% (at-most-once) target queue out of potentially many (e.g. million) queues might be unavailable.
- %% 2. Memory usage in this (publishing) process is kept lower because the target queue name can be
- %% directly removed from rabbit_mqtt_confirms and rabbit_confirms.
- %% 3. Reduced network traffic across RabbitMQ nodes.
- %% 4. Lower latency of sending publisher confirmation back to the publishing client.
- SeqNos = [SeqNo],
- lists:mapfoldl(fun({Q, stateless}, Actions) ->
- {amqqueue:get_pid(Q),
- [{settled, amqqueue:get_name(Q), SeqNos} | Actions]}
- end, [], Qs)
- end,
+ case Confirm of
+ false ->
+ Pids0 = lists:map(fun({Q, stateless}) -> amqqueue:get_pid(Q) end, Qs),
+ {Pids0, []};
+ true ->
+ %% We confirm the message directly here in the queue client.
+ %% Alternatively, we could have the target MQTT connection process confirm the message.
+ %% However, given that this message might be lost anyway between target MQTT connection
+ %% process and MQTT subscriber, and we know that the MQTT subscriber wants to receive
+ %% this message at most once, we confirm here directly.
+ %% Benefits:
+ %% 1. We do not block sending the confirmation back to the publishing client just because a single
+ %% (at-most-once) target queue out of potentially many (e.g. million) queues might be unavailable.
+ %% 2. Memory usage in this (publishing) process is kept lower because the target queue name can be
+ %% directly removed from rabbit_mqtt_confirms and rabbit_confirms.
+ %% 3. Reduced network traffic across RabbitMQ nodes.
+ %% 4. Lower latency of sending publisher confirmation back to the publishing client.
+ SeqNos = [SeqNo],
+ lists:mapfoldl(
+ fun({Q, stateless}, Actions) ->
+ {amqqueue:get_pid(Q), [{settled, amqqueue:get_name(Q), SeqNos} | Actions]}
+ end,
+ [],
+ Qs
+ )
+ end,
delegate:invoke_no_result(Pids, {gen_server, cast, [Msg]}),
{[], Actions}.
@@ -152,8 +172,8 @@ is_recoverable(Q) ->
Pid = amqqueue:get_pid(Q),
OwnerPid = amqqueue:get_exclusive_owner(Q),
node() =:= node(Pid) andalso
- Pid =:= OwnerPid andalso
- not is_process_alive(Pid).
+ Pid =:= OwnerPid andalso
+ not is_process_alive(Pid).
%% We (mis)use the recover callback to clean up our exclusive queues
%% which otherwise do not get cleaned up after a node crash.
@@ -161,20 +181,24 @@ is_recoverable(Q) ->
{Recovered :: [amqqueue:amqqueue()], Failed :: [amqqueue:amqqueue()]}.
recover(_VHost, Queues) ->
lists:foreach(
- fun(Q) ->
- %% sanity check
- true = is_recoverable(Q),
- QName = amqqueue:get_name(Q),
- log_delete(QName, amqqueue:get_exclusive_owner(Q)),
- rabbit_amqqueue:internal_delete(QName, ?INTERNAL_USER)
- end, Queues),
+ fun(Q) ->
+ %% sanity check
+ true = is_recoverable(Q),
+ QName = amqqueue:get_name(Q),
+ log_delete(QName, amqqueue:get_exclusive_owner(Q)),
+ rabbit_amqqueue:internal_delete(QName, ?INTERNAL_USER)
+ end,
+ Queues
+ ),
%% We mark the queue recovery as failed because these queues are not really
%% recovered, but deleted.
{[], Queues}.
log_delete(QName, ConPid) ->
- rabbit_log_queue:debug("Deleting ~s of type ~s because its declaring connection ~tp was closed",
- [rabbit_misc:rs(QName), ?MODULE, ConPid]).
+ rabbit_log_queue:debug(
+ "Deleting ~s of type ~s because its declaring connection ~tp was closed",
+ [rabbit_misc:rs(QName), ?MODULE, ConPid]
+ ).
-spec purge(amqqueue:amqqueue()) ->
{ok, non_neg_integer()}.
@@ -203,11 +227,13 @@ capabilities() ->
-spec info(amqqueue:amqqueue(), all_keys | rabbit_types:info_keys()) ->
rabbit_types:infos().
-info(Q, all_keys)
- when ?is_amqqueue(Q) ->
+info(Q, all_keys) when
+ ?is_amqqueue(Q)
+->
info(Q, ?INFO_KEYS);
-info(Q, Items)
- when ?is_amqqueue(Q) ->
+info(Q, Items) when
+ ?is_amqqueue(Q)
+->
[{Item, i(Item, Q)} || Item <- Items].
i(type, _) ->
@@ -249,26 +275,26 @@ init(A1) ->
close(A1) ->
?UNSUPPORTED([A1]).
-update(A1,A2) ->
- ?UNSUPPORTED([A1,A2]).
+update(A1, A2) ->
+ ?UNSUPPORTED([A1, A2]).
-consume(A1,A2,A3) ->
- ?UNSUPPORTED([A1,A2,A3]).
+consume(A1, A2, A3) ->
+ ?UNSUPPORTED([A1, A2, A3]).
-cancel(A1,A2,A3,A4,A5) ->
- ?UNSUPPORTED([A1,A2,A3,A4,A5]).
+cancel(A1, A2, A3, A4, A5) ->
+ ?UNSUPPORTED([A1, A2, A3, A4, A5]).
-handle_event(A1,A2,A3) ->
- ?UNSUPPORTED([A1,A2,A3]).
+handle_event(A1, A2, A3) ->
+ ?UNSUPPORTED([A1, A2, A3]).
-settle(A1,A2,A3,A4,A5) ->
- ?UNSUPPORTED([A1,A2,A3,A4,A5]).
+settle(A1, A2, A3, A4, A5) ->
+ ?UNSUPPORTED([A1, A2, A3, A4, A5]).
-credit(A1,A2,A3,A4,A5) ->
- ?UNSUPPORTED([A1,A2,A3,A4,A5]).
+credit(A1, A2, A3, A4, A5) ->
+ ?UNSUPPORTED([A1, A2, A3, A4, A5]).
-dequeue(A1,A2,A3,A4,A5) ->
- ?UNSUPPORTED([A1,A2,A3,A4,A5]).
+dequeue(A1, A2, A3, A4, A5) ->
+ ?UNSUPPORTED([A1, A2, A3, A4, A5]).
state_info(A1) ->
?UNSUPPORTED([A1]).
diff --git a/deps/rabbitmq_mqtt/src/rabbit_mqtt_reader.erl b/deps/rabbitmq_mqtt/src/rabbit_mqtt_reader.erl
index 3949989230..6f32816492 100644
--- a/deps/rabbitmq_mqtt/src/rabbit_mqtt_reader.erl
+++ b/deps/rabbitmq_mqtt/src/rabbit_mqtt_reader.erl
@@ -14,11 +14,20 @@
-include_lib("rabbit_common/include/logging.hrl").
-export([start_link/3]).
--export([init/1, handle_call/3, handle_cast/2, handle_info/2,
- code_change/3, terminate/2, format_status/1]).
-
--export([conserve_resources/3,
- close_connection/2]).
+-export([
+ init/1,
+ handle_call/3,
+ handle_cast/2,
+ handle_info/2,
+ code_change/3,
+ terminate/2,
+ format_status/1
+]).
+
+-export([
+ conserve_resources/3,
+ close_connection/2
+]).
-export([info/2]).
@@ -29,22 +38,22 @@
-define(HIBERNATE_AFTER, 1000).
-define(PROTO_FAMILY, 'MQTT').
--record(state,
- {socket :: rabbit_net:socket(),
- proxy_socket :: option({rabbit_proxy_socket, any(), any()}),
- await_recv :: boolean(),
- deferred_recv :: option(binary()),
- parse_state :: rabbit_mqtt_packet:state(),
- proc_state :: rabbit_mqtt_processor:state(),
- connection_state :: running | blocked,
- conserve :: boolean(),
- stats_timer :: option(rabbit_event:state()),
- keepalive = rabbit_mqtt_keepalive:init() :: rabbit_mqtt_keepalive:state(),
- conn_name :: binary(),
- received_connect_packet :: boolean()
- }).
-
--type(state() :: #state{}).
+-record(state, {
+ socket :: rabbit_net:socket(),
+ proxy_socket :: option({rabbit_proxy_socket, any(), any()}),
+ await_recv :: boolean(),
+ deferred_recv :: option(binary()),
+ parse_state :: rabbit_mqtt_packet:state(),
+ proc_state :: rabbit_mqtt_processor:state(),
+ connection_state :: running | blocked,
+ conserve :: boolean(),
+ stats_timer :: option(rabbit_event:state()),
+ keepalive = rabbit_mqtt_keepalive:init() :: rabbit_mqtt_keepalive:state(),
+ conn_name :: binary(),
+ received_connect_packet :: boolean()
+}).
+
+-type state() :: #state{}.
%%----------------------------------------------------------------------------
@@ -52,9 +61,11 @@ start_link(Ref, _Transport, []) ->
Pid = proc_lib:spawn_link(?MODULE, init, [Ref]),
{ok, Pid}.
--spec conserve_resources(pid(),
- rabbit_alarm:resource_alarm_source(),
- rabbit_alarm:resource_alert()) -> ok.
+-spec conserve_resources(
+ pid(),
+ rabbit_alarm:resource_alarm_source(),
+ rabbit_alarm:resource_alert()
+) -> ok.
conserve_resources(Pid, _, {_, Conserve, _}) ->
Pid ! {conserve_resources, Conserve},
ok.
@@ -73,8 +84,10 @@ close_connection(Pid, Reason) ->
init(Ref) ->
process_flag(trap_exit, true),
logger:set_process_metadata(#{domain => ?RMQLOG_DOMAIN_CONN ++ [mqtt]}),
- {ok, Sock} = rabbit_networking:handshake(Ref,
- application:get_env(?APP_NAME, proxy_protocol, false)),
+ {ok, Sock} = rabbit_networking:handshake(
+ Ref,
+ application:get_env(?APP_NAME, proxy_protocol, false)
+ ),
RealSocket = rabbit_net:unwrap_socket(Sock),
case rabbit_net:connection_string(Sock, inbound) of
{ok, ConnStr} ->
@@ -84,15 +97,17 @@ init(Ref) ->
LoginTimeout = application:get_env(?APP_NAME, login_timeout, 10_000),
erlang:send_after(LoginTimeout, self(), login_timeout),
ProcessorState = rabbit_mqtt_processor:initial_state(RealSocket, ConnName),
- State0 = #state{socket = RealSocket,
- proxy_socket = rabbit_net:maybe_get_proxy_socket(Sock),
- conn_name = ConnName,
- await_recv = false,
- connection_state = running,
- received_connect_packet = false,
- conserve = false,
- parse_state = rabbit_mqtt_packet:initial_state(),
- proc_state = ProcessorState},
+ State0 = #state{
+ socket = RealSocket,
+ proxy_socket = rabbit_net:maybe_get_proxy_socket(Sock),
+ conn_name = ConnName,
+ await_recv = false,
+ connection_state = running,
+ received_connect_packet = false,
+ conserve = false,
+ parse_state = rabbit_mqtt_packet:initial_state(),
+ proc_state = ProcessorState
+ },
State1 = control_throttle(State0),
State = rabbit_event:init_stats_timer(State1, #state.stats_timer),
gen_server:enter_loop(?MODULE, [], State);
@@ -108,51 +123,67 @@ init(Ref) ->
handle_call({info, InfoItems}, _From, State) ->
{reply, infos(InfoItems, State), State, ?HIBERNATE_AFTER};
-
handle_call(Msg, From, State) ->
{stop, {mqtt_unexpected_call, Msg, From}, State}.
-handle_cast(duplicate_id,
- State = #state{ proc_state = PState,
- conn_name = ConnName }) ->
- ?LOG_WARNING("MQTT disconnecting client ~tp with duplicate id '~ts'",
- [ConnName, rabbit_mqtt_processor:info(client_id, PState)]),
+handle_cast(
+ duplicate_id,
+ State = #state{
+ proc_state = PState,
+ conn_name = ConnName
+ }
+) ->
+ ?LOG_WARNING(
+ "MQTT disconnecting client ~tp with duplicate id '~ts'",
+ [ConnName, rabbit_mqtt_processor:info(client_id, PState)]
+ ),
{stop, {shutdown, duplicate_id}, State};
-
-handle_cast(decommission_node,
- State = #state{ proc_state = PState,
- conn_name = ConnName }) ->
- ?LOG_WARNING("MQTT disconnecting client ~tp with client ID '~ts' as its node is about"
- " to be decommissioned",
- [ConnName, rabbit_mqtt_processor:info(client_id, PState)]),
+handle_cast(
+ decommission_node,
+ State = #state{
+ proc_state = PState,
+ conn_name = ConnName
+ }
+) ->
+ ?LOG_WARNING(
+ "MQTT disconnecting client ~tp with client ID '~ts' as its node is about"
+ " to be decommissioned",
+ [ConnName, rabbit_mqtt_processor:info(client_id, PState)]
+ ),
{stop, {shutdown, decommission_node}, State};
-
-handle_cast({close_connection, Reason},
- State = #state{conn_name = ConnName, proc_state = PState}) ->
- ?LOG_WARNING("MQTT disconnecting client ~tp with client ID '~ts', reason: ~ts",
- [ConnName, rabbit_mqtt_processor:info(client_id, PState), Reason]),
+handle_cast(
+ {close_connection, Reason},
+ State = #state{conn_name = ConnName, proc_state = PState}
+) ->
+ ?LOG_WARNING(
+ "MQTT disconnecting client ~tp with client ID '~ts', reason: ~ts",
+ [ConnName, rabbit_mqtt_processor:info(client_id, PState), Reason]
+ ),
{stop, {shutdown, server_initiated_close}, State};
-
-handle_cast(QueueEvent = {queue_event, _, _},
- State = #state{proc_state = PState0}) ->
+handle_cast(
+ QueueEvent = {queue_event, _, _},
+ State = #state{proc_state = PState0}
+) ->
case rabbit_mqtt_processor:handle_queue_event(QueueEvent, PState0) of
{ok, PState} ->
maybe_process_deferred_recv(control_throttle(pstate(State, PState)));
{error, Reason, PState} ->
{stop, Reason, pstate(State, PState)}
end;
-
handle_cast({force_event_refresh, Ref}, State0) ->
Infos = infos(?CREATION_EVENT_KEYS, State0),
rabbit_event:notify(connection_created, Infos, Ref),
State = rabbit_event:init_stats_timer(State0, #state.stats_timer),
{noreply, State, ?HIBERNATE_AFTER};
-
-handle_cast(refresh_config, State = #state{proc_state = PState0,
- conn_name = ConnName}) ->
+handle_cast(
+ refresh_config,
+ State = #state{
+ proc_state = PState0,
+ conn_name = ConnName
+ }
+) ->
PState = rabbit_mqtt_processor:update_trace(ConnName, PState0),
{noreply, pstate(State, PState), ?HIBERNATE_AFTER};
-
handle_cast(Msg, State) ->
{stop, {mqtt_unexpected_cast, Msg}, State}.
@@ -161,49 +192,53 @@ handle_info(connection_created, State) ->
rabbit_core_metrics:connection_created(self(), Infos),
rabbit_event:notify(connection_created, Infos),
{noreply, State, ?HIBERNATE_AFTER};
-
handle_info(timeout, State) ->
rabbit_mqtt_processor:handle_pre_hibernate(),
{noreply, State, hibernate};
-
handle_info({'EXIT', _Conn, Reason}, State) ->
{stop, {connection_died, Reason}, State};
-
-handle_info({Tag, Sock, Data},
- State = #state{ socket = Sock, connection_state = blocked })
- when Tag =:= tcp; Tag =:= ssl ->
- {noreply, State#state{ deferred_recv = Data }, ?HIBERNATE_AFTER};
-
-handle_info({Tag, Sock, Data},
- State = #state{ socket = Sock, connection_state = running })
- when Tag =:= tcp; Tag =:= ssl ->
+handle_info(
+ {Tag, Sock, Data},
+ State = #state{socket = Sock, connection_state = blocked}
+) when
+ Tag =:= tcp; Tag =:= ssl
+->
+ {noreply, State#state{deferred_recv = Data}, ?HIBERNATE_AFTER};
+handle_info(
+ {Tag, Sock, Data},
+ State = #state{socket = Sock, connection_state = running}
+) when
+ Tag =:= tcp; Tag =:= ssl
+->
process_received_bytes(
- Data, control_throttle(State #state{ await_recv = false }));
-
-handle_info({Tag, Sock}, State = #state{socket = Sock})
- when Tag =:= tcp_closed; Tag =:= ssl_closed ->
+ Data, control_throttle(State#state{await_recv = false})
+ );
+handle_info({Tag, Sock}, State = #state{socket = Sock}) when
+ Tag =:= tcp_closed; Tag =:= ssl_closed
+->
network_error(closed, State);
-
-handle_info({Tag, Sock, Reason}, State = #state{socket = Sock})
- when Tag =:= tcp_error; Tag =:= ssl_error ->
+handle_info({Tag, Sock, Reason}, State = #state{socket = Sock}) when
+ Tag =:= tcp_error; Tag =:= ssl_error
+->
network_error(Reason, State);
-
handle_info({inet_reply, Sock, ok}, State = #state{socket = Sock}) ->
{noreply, State, ?HIBERNATE_AFTER};
-
handle_info({inet_reply, Sock, {error, Reason}}, State = #state{socket = Sock}) ->
network_error(Reason, State);
-
handle_info({conserve_resources, Conserve}, State) ->
maybe_process_deferred_recv(
- control_throttle(State #state{ conserve = Conserve }));
-
+ control_throttle(State#state{conserve = Conserve})
+ );
handle_info({bump_credit, Msg}, State) ->
credit_flow:handle_bump_msg(Msg),
maybe_process_deferred_recv(control_throttle(State));
-
-handle_info({keepalive, Req}, State = #state{keepalive = KState0,
- conn_name = ConnName}) ->
+handle_info(
+ {keepalive, Req},
+ State = #state{
+ keepalive = KState0,
+ conn_name = ConnName
+ }
+) ->
case rabbit_mqtt_keepalive:handle(Req, KState0) of
{ok, KState} ->
{noreply, State#state{keepalive = KState}, ?HIBERNATE_AFTER};
@@ -213,7 +248,6 @@ handle_info({keepalive, Req}, State = #state{keepalive = KState0,
{error, Reason} ->
{stop, Reason, State}
end;
-
handle_info(login_timeout, State = #state{received_connect_packet = true}) ->
{noreply, State, ?HIBERNATE_AFTER};
handle_info(login_timeout, State = #state{conn_name = ConnName}) ->
@@ -224,43 +258,47 @@ handle_info(login_timeout, State = #state{conn_name = ConnName}) ->
%% and we don't want to skip closing the connection in that case.
?LOG_ERROR("closing MQTT connection ~tp (login timeout)", [ConnName]),
{stop, {shutdown, login_timeout}, State};
-
handle_info(emit_stats, State) ->
{noreply, emit_stats(State), ?HIBERNATE_AFTER};
-
-handle_info({ra_event, _From, Evt},
- #state{proc_state = PState0} = State) ->
+handle_info(
+ {ra_event, _From, Evt},
+ #state{proc_state = PState0} = State
+) ->
%% handle applied event to ensure registration command actually got applied
%% handle not_leader notification in case we send the command to a non-leader
PState = rabbit_mqtt_processor:handle_ra_event(Evt, PState0),
{noreply, pstate(State, PState), ?HIBERNATE_AFTER};
-
-handle_info({{'DOWN', _QName}, _MRef, process, _Pid, _Reason} = Evt,
- #state{proc_state = PState0} = State) ->
+handle_info(
+ {{'DOWN', _QName}, _MRef, process, _Pid, _Reason} = Evt,
+ #state{proc_state = PState0} = State
+) ->
case rabbit_mqtt_processor:handle_down(Evt, PState0) of
{ok, PState} ->
maybe_process_deferred_recv(control_throttle(pstate(State, PState)));
{error, Reason} ->
{stop, {shutdown, Reason, State}}
end;
-
handle_info({'DOWN', _MRef, process, QPid, _Reason}, State) ->
rabbit_amqqueue_common:notify_sent_queue_down(QPid),
{noreply, State, ?HIBERNATE_AFTER};
-
handle_info({shutdown, Explanation} = Reason, State = #state{conn_name = ConnName}) ->
%% rabbitmq_management plugin requests to close connection.
?LOG_INFO("MQTT closing connection ~tp: ~p", [ConnName, Explanation]),
{stop, Reason, State};
-
handle_info(Msg, State) ->
{stop, {mqtt_unexpected_msg, Msg}, State}.
terminate(Reason, State = #state{}) ->
terminate(Reason, {true, State});
-terminate(Reason, {SendWill, State = #state{conn_name = ConnName,
- keepalive = KState0,
- proc_state = PState}}) ->
+terminate(
+ Reason,
+ {SendWill,
+ State = #state{
+ conn_name = ConnName,
+ keepalive = KState0,
+ proc_state = PState
+ }}
+) ->
KState = rabbit_mqtt_keepalive:cancel_timer(KState0),
maybe_emit_stats(State#state{keepalive = KState}),
_ = rabbit_mqtt_processor:terminate(SendWill, ConnName, ?PROTO_FAMILY, PState),
@@ -268,36 +306,25 @@ terminate(Reason, {SendWill, State = #state{conn_name = ConnName,
log_terminate({network_error, {ssl_upgrade_error, closed}, ConnName}, _State) ->
?LOG_ERROR("MQTT detected TLS upgrade error on ~s: connection closed", [ConnName]);
-
-log_terminate({network_error,
- {ssl_upgrade_error,
- {tls_alert, "handshake failure"}}, ConnName}, _State) ->
+log_terminate(
+ {network_error, {ssl_upgrade_error, {tls_alert, "handshake failure"}}, ConnName}, _State
+) ->
log_tls_alert(handshake_failure, ConnName);
-log_terminate({network_error,
- {ssl_upgrade_error,
- {tls_alert, "unknown ca"}}, ConnName}, _State) ->
+log_terminate({network_error, {ssl_upgrade_error, {tls_alert, "unknown ca"}}, ConnName}, _State) ->
log_tls_alert(unknown_ca, ConnName);
-log_terminate({network_error,
- {ssl_upgrade_error,
- {tls_alert, {Err, _}}}, ConnName}, _State) ->
+log_terminate({network_error, {ssl_upgrade_error, {tls_alert, {Err, _}}}, ConnName}, _State) ->
log_tls_alert(Err, ConnName);
-log_terminate({network_error,
- {ssl_upgrade_error,
- {tls_alert, Alert}}, ConnName}, _State) ->
+log_terminate({network_error, {ssl_upgrade_error, {tls_alert, Alert}}, ConnName}, _State) ->
log_tls_alert(Alert, ConnName);
log_terminate({network_error, {ssl_upgrade_error, Reason}, ConnName}, _State) ->
?LOG_ERROR("MQTT detected TLS upgrade error on ~s: ~p", [ConnName, Reason]);
-
log_terminate({network_error, Reason, ConnName}, _State) ->
?LOG_ERROR("MQTT detected network error on ~s: ~p", [ConnName, Reason]);
-
log_terminate({network_error, Reason}, _State) ->
?LOG_ERROR("MQTT detected network error: ~p", [Reason]);
-
-log_terminate(normal, #state{conn_name = ConnName}) ->
+log_terminate(normal, #state{conn_name = ConnName}) ->
?LOG_INFO("closing MQTT connection ~p (~s)", [self(), ConnName]),
ok;
-
log_terminate(_Reason, _State) ->
ok.
@@ -309,54 +336,80 @@ code_change(_OldVsn, State, _Extra) ->
log_tls_alert(handshake_failure, ConnName) ->
?LOG_ERROR("MQTT detected TLS upgrade error on ~ts: handshake failure", [ConnName]);
log_tls_alert(unknown_ca, ConnName) ->
- ?LOG_ERROR("MQTT detected TLS certificate verification error on ~ts: alert 'unknown CA'",
- [ConnName]);
+ ?LOG_ERROR(
+ "MQTT detected TLS certificate verification error on ~ts: alert 'unknown CA'",
+ [ConnName]
+ );
log_tls_alert(Alert, ConnName) ->
?LOG_ERROR("MQTT detected TLS upgrade error on ~ts: alert ~ts", [ConnName, Alert]).
-process_received_bytes(<<>>, State = #state{received_connect_packet = false,
- proc_state = PState,
- conn_name = ConnName}) ->
- ?LOG_INFO("Accepted MQTT connection ~p (~s, client ID: ~s)",
- [self(), ConnName, rabbit_mqtt_processor:info(client_id, PState)]),
+process_received_bytes(
+ <<>>,
+ State = #state{
+ received_connect_packet = false,
+ proc_state = PState,
+ conn_name = ConnName
+ }
+) ->
+ ?LOG_INFO(
+ "Accepted MQTT connection ~p (~s, client ID: ~s)",
+ [self(), ConnName, rabbit_mqtt_processor:info(client_id, PState)]
+ ),
{noreply, ensure_stats_timer(State#state{received_connect_packet = true}), ?HIBERNATE_AFTER};
process_received_bytes(<<>>, State) ->
{noreply, ensure_stats_timer(State), ?HIBERNATE_AFTER};
-process_received_bytes(Bytes,
- State = #state{ parse_state = ParseState,
- proc_state = ProcState,
- conn_name = ConnName }) ->
+process_received_bytes(
+ Bytes,
+ State = #state{
+ parse_state = ParseState,
+ proc_state = ProcState,
+ conn_name = ConnName
+ }
+) ->
case parse(Bytes, ParseState) of
{more, ParseState1} ->
- {noreply,
- ensure_stats_timer( State #state{ parse_state = ParseState1 }),
- ?HIBERNATE_AFTER};
+ {noreply, ensure_stats_timer(State#state{parse_state = ParseState1}), ?HIBERNATE_AFTER};
{ok, Packet, Rest} ->
case rabbit_mqtt_processor:process_packet(Packet, ProcState) of
{ok, ProcState1} ->
process_received_bytes(
- Rest,
- State #state{parse_state = rabbit_mqtt_packet:initial_state(),
- proc_state = ProcState1});
+ Rest,
+ State#state{
+ parse_state = rabbit_mqtt_packet:initial_state(),
+ proc_state = ProcState1
+ }
+ );
%% PUBLISH and more
{error, unauthorized = Reason, ProcState1} ->
- ?LOG_ERROR("MQTT connection ~ts is closing due to an authorization failure", [ConnName]),
+ ?LOG_ERROR("MQTT connection ~ts is closing due to an authorization failure", [
+ ConnName
+ ]),
{stop, {shutdown, Reason}, pstate(State, ProcState1)};
%% CONNECT packets only
{error, unauthenticated = Reason, ProcState1} ->
- ?LOG_ERROR("MQTT connection ~ts is closing due to an authentication failure", [ConnName]),
+ ?LOG_ERROR("MQTT connection ~ts is closing due to an authentication failure", [
+ ConnName
+ ]),
{stop, {shutdown, Reason}, pstate(State, ProcState1)};
%% CONNECT packets only
{error, invalid_client_id = Reason, ProcState1} ->
- ?LOG_ERROR("MQTT cannot accept connection ~ts: client uses an invalid ID", [ConnName]),
+ ?LOG_ERROR("MQTT cannot accept connection ~ts: client uses an invalid ID", [
+ ConnName
+ ]),
{stop, {shutdown, Reason}, pstate(State, ProcState1)};
%% CONNECT packets only
{error, unsupported_protocol_version = Reason, ProcState1} ->
- ?LOG_ERROR("MQTT cannot accept connection ~ts: incompatible protocol version", [ConnName]),
+ ?LOG_ERROR(
+ "MQTT cannot accept connection ~ts: incompatible protocol version", [
+ ConnName
+ ]
+ ),
{stop, {shutdown, Reason}, pstate(State, ProcState1)};
{error, unavailable = Reason, ProcState1} ->
- ?LOG_ERROR("MQTT cannot accept connection ~ts due to an internal error or unavailable component",
- [ConnName]),
+ ?LOG_ERROR(
+ "MQTT cannot accept connection ~ts due to an internal error or unavailable component",
+ [ConnName]
+ ),
{stop, {shutdown, Reason}, pstate(State, ProcState1)};
{error, Reason, ProcState1} ->
?LOG_ERROR("MQTT protocol error on connection ~ts: ~tp", [ConnName, Reason]),
@@ -365,9 +418,11 @@ process_received_bytes(Bytes,
{stop, normal, {_SendWill = false, pstate(State, ProcState1)}}
end;
{error, {cannot_parse, Reason, Stacktrace}} ->
- ?LOG_ERROR("MQTT cannot parse a packet on connection '~ts', reason: ~tp, "
- "stacktrace: ~tp, payload (first 100 bytes): ~tp",
- [ConnName, Reason, Stacktrace, rabbit_mqtt_util:truncate_binary(Bytes, 100)]),
+ ?LOG_ERROR(
+ "MQTT cannot parse a packet on connection '~ts', reason: ~tp, "
+ "stacktrace: ~tp, payload (first 100 bytes): ~tp",
+ [ConnName, Reason, Stacktrace, rabbit_mqtt_util:truncate_binary(Bytes, 100)]
+ ),
{stop, {shutdown, Reason}, State};
{error, Error} ->
?LOG_ERROR("MQTT detected a framing error on connection ~ts: ~tp", [ConnName, Error]),
@@ -375,8 +430,8 @@ process_received_bytes(Bytes,
end.
-spec pstate(state(), rabbit_mqtt_processor:state()) -> state().
-pstate(State = #state {}, PState) ->
- State #state{ proc_state = PState }.
+pstate(State = #state{}, PState) ->
+ State#state{proc_state = PState}.
%%----------------------------------------------------------------------------
parse(Bytes, ParseState) ->
@@ -387,9 +442,13 @@ parse(Bytes, ParseState) ->
{error, {cannot_parse, Reason, Stacktrace}}
end.
-network_error(closed,
- State = #state{conn_name = ConnName,
- received_connect_packet = Connected}) ->
+network_error(
+ closed,
+ State = #state{
+ conn_name = ConnName,
+ received_connect_packet = Connected
+ }
+) ->
Fmt = "MQTT connection ~p will terminate because peer closed TCP connection",
Args = [ConnName],
case Connected of
@@ -397,62 +456,77 @@ network_error(closed,
false -> ?LOG_DEBUG(Fmt, Args)
end,
{stop, {shutdown, conn_closed}, State};
-
-network_error(Reason,
- State = #state{conn_name = ConnName}) ->
+network_error(
+ Reason,
+ State = #state{conn_name = ConnName}
+) ->
?LOG_INFO("MQTT detected network error for ~p: ~p", [ConnName, Reason]),
{stop, {shutdown, conn_closed}, State}.
-run_socket(State = #state{ connection_state = blocked }) ->
+run_socket(State = #state{connection_state = blocked}) ->
State;
-run_socket(State = #state{ deferred_recv = Data }) when Data =/= undefined ->
+run_socket(State = #state{deferred_recv = Data}) when Data =/= undefined ->
State;
-run_socket(State = #state{ await_recv = true }) ->
+run_socket(State = #state{await_recv = true}) ->
State;
-run_socket(State = #state{ socket = Sock }) ->
+run_socket(State = #state{socket = Sock}) ->
ok = rabbit_net:setopts(Sock, [{active, once}]),
- State#state{ await_recv = true }.
-
-control_throttle(State = #state{connection_state = ConnState,
- conserve = Conserve,
- received_connect_packet = Connected,
- proc_state = PState,
- keepalive = KState
- }) ->
+ State#state{await_recv = true}.
+
+control_throttle(
+ State = #state{
+ connection_state = ConnState,
+ conserve = Conserve,
+ received_connect_packet = Connected,
+ proc_state = PState,
+ keepalive = KState
+ }
+) ->
Throttle = rabbit_mqtt_processor:throttle(Conserve, Connected, PState),
case {ConnState, Throttle} of
{running, true} ->
- State#state{connection_state = blocked,
- keepalive = rabbit_mqtt_keepalive:cancel_timer(KState)};
+ State#state{
+ connection_state = blocked,
+ keepalive = rabbit_mqtt_keepalive:cancel_timer(KState)
+ };
{blocked, false} ->
- run_socket(State#state{connection_state = running,
- keepalive = rabbit_mqtt_keepalive:start_timer(KState)});
+ run_socket(State#state{
+ connection_state = running,
+ keepalive = rabbit_mqtt_keepalive:start_timer(KState)
+ });
{_, _} ->
run_socket(State)
end.
-maybe_process_deferred_recv(State = #state{ deferred_recv = undefined }) ->
+maybe_process_deferred_recv(State = #state{deferred_recv = undefined}) ->
{noreply, State, ?HIBERNATE_AFTER};
-maybe_process_deferred_recv(State = #state{ deferred_recv = Data, socket = Sock }) ->
- handle_info({tcp, Sock, Data},
- State#state{ deferred_recv = undefined }).
+maybe_process_deferred_recv(State = #state{deferred_recv = Data, socket = Sock}) ->
+ handle_info(
+ {tcp, Sock, Data},
+ State#state{deferred_recv = undefined}
+ ).
maybe_emit_stats(#state{stats_timer = undefined}) ->
ok;
maybe_emit_stats(State) ->
- rabbit_event:if_enabled(State, #state.stats_timer,
- fun() -> emit_stats(State) end).
+ rabbit_event:if_enabled(
+ State,
+ #state.stats_timer,
+ fun() -> emit_stats(State) end
+ ).
-emit_stats(State=#state{received_connect_packet = false}) ->
+emit_stats(State = #state{received_connect_packet = false}) ->
%% Avoid emitting stats on terminate when the connection has not yet been
%% established, as this causes orphan entries on the stats database
State1 = rabbit_event:reset_stats_timer(State, #state.stats_timer),
ensure_stats_timer(State1);
emit_stats(State) ->
- [{_, Pid},
- {_, RecvOct},
- {_, SendOct},
- {_, Reductions}] = infos(?SIMPLE_METRICS, State),
+ [
+ {_, Pid},
+ {_, RecvOct},
+ {_, SendOct},
+ {_, Reductions}
+ ] = infos(?SIMPLE_METRICS, State),
Infos = infos(?OTHER_METRICS, State),
rabbit_core_metrics:connection_stats(Pid, Infos),
rabbit_core_metrics:connection_stats(Pid, RecvOct, SendOct, Reductions),
@@ -465,12 +539,13 @@ ensure_stats_timer(State = #state{}) ->
infos(Items, State) ->
[{Item, i(Item, State)} || Item <- Items].
-i(SockStat, #state{socket = Sock})
- when SockStat =:= recv_oct;
- SockStat =:= recv_cnt;
- SockStat =:= send_oct;
- SockStat =:= send_cnt;
- SockStat =:= send_pend ->
+i(SockStat, #state{socket = Sock}) when
+ SockStat =:= recv_oct;
+ SockStat =:= recv_cnt;
+ SockStat =:= send_oct;
+ SockStat =:= send_cnt;
+ SockStat =:= send_pend
+->
case rabbit_net:getstat(Sock, [SockStat]) of
{ok, [{_, N}]} when is_number(N) ->
N;
@@ -494,17 +569,19 @@ i(connection_state, #state{connection_state = Val}) ->
Val;
i(pid, _) ->
self();
-i(SSL, #state{socket = Sock, proxy_socket = ProxySock})
- when SSL =:= ssl;
- SSL =:= ssl_protocol;
- SSL =:= ssl_key_exchange;
- SSL =:= ssl_cipher;
- SSL =:= ssl_hash ->
+i(SSL, #state{socket = Sock, proxy_socket = ProxySock}) when
+ SSL =:= ssl;
+ SSL =:= ssl_protocol;
+ SSL =:= ssl_key_exchange;
+ SSL =:= ssl_cipher;
+ SSL =:= ssl_hash
+->
rabbit_ssl:info(SSL, {Sock, ProxySock});
-i(Cert, #state{socket = Sock})
- when Cert =:= peer_cert_issuer;
- Cert =:= peer_cert_subject;
- Cert =:= peer_cert_validity ->
+i(Cert, #state{socket = Sock}) when
+ Cert =:= peer_cert_issuer;
+ Cert =:= peer_cert_subject;
+ Cert =:= peer_cert_validity
+->
rabbit_ssl:cert_info(Cert, Sock);
i(timeout, #state{keepalive = KState}) ->
rabbit_mqtt_keepalive:interval_secs(KState);
@@ -514,40 +591,48 @@ i(Key, #state{proc_state = ProcState}) ->
rabbit_mqtt_processor:info(Key, ProcState).
-spec format_status(Status) -> Status when
- Status :: #{state => term(),
- message => term(),
- reason => term(),
- log => [sys:system_event()]}.
+ Status :: #{
+ state => term(),
+ message => term(),
+ reason => term(),
+ log => [sys:system_event()]
+ }.
format_status(Status) ->
maps:map(
- fun(state, State) ->
- format_state(State);
- (_, Value) ->
- Value
- end, Status).
+ fun
+ (state, State) ->
+ format_state(State);
+ (_, Value) ->
+ Value
+ end,
+ Status
+ ).
-spec format_state(state()) -> map().
-format_state(#state{socket = Socket,
- proxy_socket = ProxySock,
- await_recv = AwaitRecv,
- deferred_recv = DeferredRecv,
- parse_state = _,
- proc_state = PState,
- connection_state = ConnectionState,
- conserve = Conserve,
- stats_timer = StatsTimer,
- keepalive = Keepalive,
- conn_name = ConnName,
- received_connect_packet = ReceivedConnectPacket
- }) ->
- #{socket => Socket,
- proxy_socket => ProxySock,
- await_recv => AwaitRecv,
- deferred_recv => DeferredRecv =/= undefined,
- proc_state => rabbit_mqtt_processor:format_status(PState),
- connection_state => ConnectionState,
- conserve => Conserve,
- stats_timer => StatsTimer,
- keepalive => Keepalive,
- conn_name => ConnName,
- received_connect_packet => ReceivedConnectPacket}.
+format_state(#state{
+ socket = Socket,
+ proxy_socket = ProxySock,
+ await_recv = AwaitRecv,
+ deferred_recv = DeferredRecv,
+ parse_state = _,
+ proc_state = PState,
+ connection_state = ConnectionState,
+ conserve = Conserve,
+ stats_timer = StatsTimer,
+ keepalive = Keepalive,
+ conn_name = ConnName,
+ received_connect_packet = ReceivedConnectPacket
+}) ->
+ #{
+ socket => Socket,
+ proxy_socket => ProxySock,
+ await_recv => AwaitRecv,
+ deferred_recv => DeferredRecv =/= undefined,
+ proc_state => rabbit_mqtt_processor:format_status(PState),
+ connection_state => ConnectionState,
+ conserve => Conserve,
+ stats_timer => StatsTimer,
+ keepalive => Keepalive,
+ conn_name => ConnName,
+ received_connect_packet => ReceivedConnectPacket
+ }.
diff --git a/deps/rabbitmq_mqtt/src/rabbit_mqtt_retained_msg_store_dets.erl b/deps/rabbitmq_mqtt/src/rabbit_mqtt_retained_msg_store_dets.erl
index 8dbd06a440..bfaa0cd146 100644
--- a/deps/rabbitmq_mqtt/src/rabbit_mqtt_retained_msg_store_dets.erl
+++ b/deps/rabbitmq_mqtt/src/rabbit_mqtt_retained_msg_store_dets.erl
@@ -13,53 +13,57 @@
-export([new/2, recover/2, insert/3, lookup/2, delete/2, terminate/1]).
-record(store_state, {
- %% DETS table name
- table
+ %% DETS table name
+ table
}).
-type store_state() :: #store_state{}.
-spec new(file:name_all(), rabbit_types:vhost()) -> store_state().
new(Dir, VHost) ->
- Tid = open_table(Dir, VHost),
- #store_state{table = Tid}.
+ Tid = open_table(Dir, VHost),
+ #store_state{table = Tid}.
-spec recover(file:name_all(), rabbit_types:vhost()) ->
- {error, uninitialized} | {ok, store_state()}.
+ {error, uninitialized} | {ok, store_state()}.
recover(Dir, VHost) ->
- case open_table(Dir, VHost) of
- {error, _} -> {error, uninitialized};
- {ok, Tid} -> {ok, #store_state{table = Tid}}
- end.
+ case open_table(Dir, VHost) of
+ {error, _} -> {error, uninitialized};
+ {ok, Tid} -> {ok, #store_state{table = Tid}}
+ end.
-spec insert(binary(), mqtt_msg(), store_state()) -> ok.
insert(Topic, Msg, #store_state{table = T}) ->
- ok = dets:insert(T, #retained_message{topic = Topic, mqtt_msg = Msg}).
+ ok = dets:insert(T, #retained_message{topic = Topic, mqtt_msg = Msg}).
-spec lookup(binary(), store_state()) -> retained_message() | not_found.
lookup(Topic, #store_state{table = T}) ->
- case dets:lookup(T, Topic) of
- [] -> not_found;
- [Entry] -> Entry
- end.
+ case dets:lookup(T, Topic) of
+ [] -> not_found;
+ [Entry] -> Entry
+ end.
-spec delete(binary(), store_state()) -> ok.
delete(Topic, #store_state{table = T}) ->
- ok = dets:delete(T, Topic).
+ ok = dets:delete(T, Topic).
-spec terminate(store_state()) -> ok.
terminate(#store_state{table = T}) ->
- ok = dets:close(T).
+ ok = dets:close(T).
open_table(Dir, VHost) ->
- dets:open_file(rabbit_mqtt_util:vhost_name_to_table_name(VHost),
- table_options(rabbit_mqtt_util:path_for(Dir, VHost, ".dets"))).
+ dets:open_file(
+ rabbit_mqtt_util:vhost_name_to_table_name(VHost),
+ table_options(rabbit_mqtt_util:path_for(Dir, VHost, ".dets"))
+ ).
table_options(Path) ->
- [{type, set},
- {keypos, #retained_message.topic},
- {file, Path},
- {ram_file, true},
- {repair, true},
- {auto_save, rabbit_misc:get_env(rabbit_mqtt, retained_message_store_dets_sync_interval, 2000)}
+ [
+ {type, set},
+ {keypos, #retained_message.topic},
+ {file, Path},
+ {ram_file, true},
+ {repair, true},
+ {auto_save,
+ rabbit_misc:get_env(rabbit_mqtt, retained_message_store_dets_sync_interval, 2000)}
].
diff --git a/deps/rabbitmq_mqtt/src/rabbit_mqtt_retained_msg_store_ets.erl b/deps/rabbitmq_mqtt/src/rabbit_mqtt_retained_msg_store_ets.erl
index 3a0a7384db..bbc7a77390 100644
--- a/deps/rabbitmq_mqtt/src/rabbit_mqtt_retained_msg_store_ets.erl
+++ b/deps/rabbitmq_mqtt/src/rabbit_mqtt_retained_msg_store_ets.erl
@@ -13,49 +13,51 @@
-export([new/2, recover/2, insert/3, lookup/2, delete/2, terminate/1]).
-record(store_state, {
- %% ETS table ID
- table,
- %% where the table is stored on disk
- filename
+ %% ETS table ID
+ table,
+ %% where the table is stored on disk
+ filename
}).
-type store_state() :: #store_state{}.
-spec new(file:name_all(), rabbit_types:vhost()) -> store_state().
new(Dir, VHost) ->
- Path = rabbit_mqtt_util:path_for(Dir, VHost),
- TableName = rabbit_mqtt_util:vhost_name_to_table_name(VHost),
- _ = file:delete(Path),
- Tid = ets:new(TableName, [set, public, {keypos, #retained_message.topic}]),
- #store_state{table = Tid, filename = Path}.
+ Path = rabbit_mqtt_util:path_for(Dir, VHost),
+ TableName = rabbit_mqtt_util:vhost_name_to_table_name(VHost),
+ _ = file:delete(Path),
+ Tid = ets:new(TableName, [set, public, {keypos, #retained_message.topic}]),
+ #store_state{table = Tid, filename = Path}.
-spec recover(file:name_all(), rabbit_types:vhost()) ->
- {error, uninitialized} | {ok, store_state()}.
+ {error, uninitialized} | {ok, store_state()}.
recover(Dir, VHost) ->
- Path = rabbit_mqtt_util:path_for(Dir, VHost),
- case ets:file2tab(Path) of
- {ok, Tid} -> _ = file:delete(Path),
- {ok, #store_state{table = Tid, filename = Path}};
- {error, _} -> {error, uninitialized}
- end.
+ Path = rabbit_mqtt_util:path_for(Dir, VHost),
+ case ets:file2tab(Path) of
+ {ok, Tid} ->
+ _ = file:delete(Path),
+ {ok, #store_state{table = Tid, filename = Path}};
+ {error, _} ->
+ {error, uninitialized}
+ end.
-spec insert(binary(), mqtt_msg(), store_state()) -> ok.
insert(Topic, Msg, #store_state{table = T}) ->
- true = ets:insert(T, #retained_message{topic = Topic, mqtt_msg = Msg}),
- ok.
+ true = ets:insert(T, #retained_message{topic = Topic, mqtt_msg = Msg}),
+ ok.
-spec lookup(binary(), store_state()) -> retained_message() | not_found.
lookup(Topic, #store_state{table = T}) ->
- case ets:lookup(T, Topic) of
- [] -> not_found;
- [Entry] -> Entry
- end.
+ case ets:lookup(T, Topic) of
+ [] -> not_found;
+ [Entry] -> Entry
+ end.
-spec delete(binary(), store_state()) -> ok.
delete(Topic, #store_state{table = T}) ->
- true = ets:delete(T, Topic),
- ok.
+ true = ets:delete(T, Topic),
+ ok.
-spec terminate(store_state()) -> ok.
terminate(#store_state{table = T, filename = Path}) ->
- ok = ets:tab2file(T, Path, [{extended_info, [object_count]}]).
+ ok = ets:tab2file(T, Path, [{extended_info, [object_count]}]).
diff --git a/deps/rabbitmq_mqtt/src/rabbit_mqtt_retained_msg_store_noop.erl b/deps/rabbitmq_mqtt/src/rabbit_mqtt_retained_msg_store_noop.erl
index 1e62ed5abe..5f6f0ef56b 100644
--- a/deps/rabbitmq_mqtt/src/rabbit_mqtt_retained_msg_store_noop.erl
+++ b/deps/rabbitmq_mqtt/src/rabbit_mqtt_retained_msg_store_noop.erl
@@ -12,19 +12,19 @@
-export([new/2, recover/2, insert/3, lookup/2, delete/2, terminate/1]).
new(_Dir, _VHost) ->
- ok.
+ ok.
recover(_Dir, _VHost) ->
- {ok, ok}.
+ {ok, ok}.
insert(_Topic, _Msg, _State) ->
- ok.
+ ok.
lookup(_Topic, _State) ->
- not_found.
+ not_found.
delete(_Topic, _State) ->
- ok.
+ ok.
terminate(_State) ->
- ok.
+ ok.
diff --git a/deps/rabbitmq_mqtt/src/rabbit_mqtt_retainer.erl b/deps/rabbitmq_mqtt/src/rabbit_mqtt_retainer.erl
index 1c632eb597..8df5d80ff7 100644
--- a/deps/rabbitmq_mqtt/src/rabbit_mqtt_retainer.erl
+++ b/deps/rabbitmq_mqtt/src/rabbit_mqtt_retainer.erl
@@ -12,15 +12,23 @@
-behaviour(gen_server).
--export([init/1, handle_call/3, handle_cast/2, handle_info/2,
- terminate/2, start_link/2]).
+-export([
+ init/1,
+ handle_call/3,
+ handle_cast/2,
+ handle_info/2,
+ terminate/2,
+ start_link/2
+]).
-export([retain/3, fetch/2, clear/2, store_module/0]).
-define(TIMEOUT, 30_000).
--record(retainer_state, {store_mod,
- store}).
+-record(retainer_state, {
+ store_mod,
+ store
+}).
%%----------------------------------------------------------------------------
@@ -46,12 +54,19 @@ clear(Pid, Topic) ->
init([StoreMod, VHost]) ->
process_flag(trap_exit, true),
- State = case StoreMod:recover(store_dir(), VHost) of
- {ok, Store} -> #retainer_state{store = Store,
- store_mod = StoreMod};
- {error, _} -> #retainer_state{store = StoreMod:new(store_dir(), VHost),
- store_mod = StoreMod}
- end,
+ State =
+ case StoreMod:recover(store_dir(), VHost) of
+ {ok, Store} ->
+ #retainer_state{
+ store = Store,
+ store_mod = StoreMod
+ };
+ {error, _} ->
+ #retainer_state{
+ store = StoreMod:new(store_dir(), VHost),
+ store_mod = StoreMod
+ }
+ end,
{ok, State}.
-spec store_module() -> undefined | module().
@@ -63,26 +78,33 @@ store_module() ->
%%----------------------------------------------------------------------------
-handle_cast({retain, Topic, Msg},
- State = #retainer_state{store = Store, store_mod = Mod}) ->
+handle_cast(
+ {retain, Topic, Msg},
+ State = #retainer_state{store = Store, store_mod = Mod}
+) ->
ok = Mod:insert(Topic, Msg, Store),
{noreply, State};
-handle_cast({clear, Topic},
- State = #retainer_state{store = Store, store_mod = Mod}) ->
+handle_cast(
+ {clear, Topic},
+ State = #retainer_state{store = Store, store_mod = Mod}
+) ->
ok = Mod:delete(Topic, Store),
{noreply, State}.
-handle_call({fetch, Topic}, _From,
- State = #retainer_state{store = Store, store_mod = Mod}) ->
- Reply = case Mod:lookup(Topic, Store) of
- #retained_message{mqtt_msg = Msg} -> Msg;
- not_found -> undefined
- end,
+handle_call(
+ {fetch, Topic},
+ _From,
+ State = #retainer_state{store = Store, store_mod = Mod}
+) ->
+ Reply =
+ case Mod:lookup(Topic, Store) of
+ #retained_message{mqtt_msg = Msg} -> Msg;
+ not_found -> undefined
+ end,
{reply, Reply, State}.
handle_info(stop, State) ->
{stop, normal, State};
-
handle_info(Info, State) ->
{stop, {unknown_info, Info}, State}.
diff --git a/deps/rabbitmq_mqtt/src/rabbit_mqtt_retainer_sup.erl b/deps/rabbitmq_mqtt/src/rabbit_mqtt_retainer_sup.erl
index eadaab7ca2..f2b2a38867 100644
--- a/deps/rabbitmq_mqtt/src/rabbit_mqtt_retainer_sup.erl
+++ b/deps/rabbitmq_mqtt/src/rabbit_mqtt_retainer_sup.erl
@@ -8,8 +8,13 @@
-module(rabbit_mqtt_retainer_sup).
-behaviour(supervisor).
--export([start_link/1, init/1, start_child/2,start_child/1, child_for_vhost/1,
- delete_child/1]).
+-export([
+ start_link/1,
+ init/1,
+ start_child/2, start_child/1,
+ child_for_vhost/1,
+ delete_child/1
+]).
-spec start_child(binary()) -> supervisor:startchild_ret().
-spec start_child(term(), binary()) -> supervisor:startchild_ret().
@@ -19,13 +24,13 @@ start_link(SupName) ->
-spec child_for_vhost(rabbit_types:vhost()) -> pid().
child_for_vhost(VHost) when is_binary(VHost) ->
- case rabbit_mqtt_retainer_sup:start_child(VHost) of
- {ok, Pid} -> Pid;
- {error, {already_started, Pid}} -> Pid
- end.
+ case rabbit_mqtt_retainer_sup:start_child(VHost) of
+ {ok, Pid} -> Pid;
+ {error, {already_started, Pid}} -> Pid
+ end.
start_child(VHost) when is_binary(VHost) ->
- start_child(rabbit_mqtt_retainer:store_module(), VHost).
+ start_child(rabbit_mqtt_retainer:store_module(), VHost).
start_child(RetainStoreMod, VHost) ->
supervisor:start_child(
@@ -41,18 +46,20 @@ start_child(RetainStoreMod, VHost) ->
).
delete_child(VHost) ->
- Id = vhost_to_atom(VHost),
- ok = supervisor:terminate_child(?MODULE, Id),
- ok = supervisor:delete_child(?MODULE, Id).
+ Id = vhost_to_atom(VHost),
+ ok = supervisor:terminate_child(?MODULE, Id),
+ ok = supervisor:delete_child(?MODULE, Id).
init([]) ->
- Mod = rabbit_mqtt_retainer:store_module(),
- rabbit_log:info("MQTT retained message store: ~tp",
- [Mod]),
- {ok, {
- #{strategy => one_for_one, intensity => 5, period => 5},
- child_specs(Mod, rabbit_vhost:list_names())
- }}.
+ Mod = rabbit_mqtt_retainer:store_module(),
+ rabbit_log:info(
+ "MQTT retained message store: ~tp",
+ [Mod]
+ ),
+ {ok, {
+ #{strategy => one_for_one, intensity => 5, period => 5},
+ child_specs(Mod, rabbit_vhost:list_names())
+ }}.
child_specs(Mod, VHosts) ->
%% see start_child/2
diff --git a/deps/rabbitmq_mqtt/src/rabbit_mqtt_sup.erl b/deps/rabbitmq_mqtt/src/rabbit_mqtt_sup.erl
index a04ef73cb9..5aef65bc2d 100644
--- a/deps/rabbitmq_mqtt/src/rabbit_mqtt_sup.erl
+++ b/deps/rabbitmq_mqtt/src/rabbit_mqtt_sup.erl
@@ -23,52 +23,61 @@ init([{Listeners, SslListeners0}]) ->
NumTcpAcceptors = application:get_env(?APP_NAME, num_tcp_acceptors, 10),
ConcurrentConnsSups = application:get_env(?APP_NAME, num_conns_sups, 1),
{ok, SocketOpts} = application:get_env(?APP_NAME, tcp_listen_options),
- {SslOpts, NumSslAcceptors, SslListeners}
- = case SslListeners0 of
- [] -> {none, 0, []};
- _ -> {rabbit_networking:ensure_ssl(),
- application:get_env(?APP_NAME, num_ssl_acceptors, 10),
- case rabbit_networking:poodle_check('MQTT') of
- ok -> SslListeners0;
- danger -> []
- end}
- end,
+ {SslOpts, NumSslAcceptors, SslListeners} =
+ case SslListeners0 of
+ [] ->
+ {none, 0, []};
+ _ ->
+ {
+ rabbit_networking:ensure_ssl(),
+ application:get_env(?APP_NAME, num_ssl_acceptors, 10),
+ case rabbit_networking:poodle_check('MQTT') of
+ ok -> SslListeners0;
+ danger -> []
+ end
+ }
+ end,
%% Use separate process group scope per RabbitMQ node. This achieves a local-only
%% process group which requires less memory with millions of connections.
PgScope = list_to_atom(io_lib:format("~s_~s", [?PG_SCOPE, node()])),
persistent_term:put(?PG_SCOPE, PgScope),
{ok,
- {#{strategy => one_for_all,
- intensity => 10,
- period => 10},
- [
- #{id => PgScope,
- start => {pg, start_link, [PgScope]},
- restart => transient,
- shutdown => ?WORKER_WAIT,
- type => worker,
- modules => [pg]
- },
- #{
- id => rabbit_mqtt_retainer_sup,
- start => {rabbit_mqtt_retainer_sup, start_link,
- [{local, rabbit_mqtt_retainer_sup}]},
- restart => transient,
- shutdown => ?SUPERVISOR_WAIT,
- type => supervisor,
- modules => [rabbit_mqtt_retainer_sup]
- }
- | listener_specs(
- fun tcp_listener_spec/1,
- [SocketOpts, NumTcpAcceptors, ConcurrentConnsSups],
- Listeners
- ) ++
- listener_specs(
- fun ssl_listener_spec/1,
- [SocketOpts, SslOpts, NumSslAcceptors, ConcurrentConnsSups],
- SslListeners
- )
- ]}}.
+ {
+ #{
+ strategy => one_for_all,
+ intensity => 10,
+ period => 10
+ },
+ [
+ #{
+ id => PgScope,
+ start => {pg, start_link, [PgScope]},
+ restart => transient,
+ shutdown => ?WORKER_WAIT,
+ type => worker,
+ modules => [pg]
+ },
+ #{
+ id => rabbit_mqtt_retainer_sup,
+ start =>
+ {rabbit_mqtt_retainer_sup, start_link, [{local, rabbit_mqtt_retainer_sup}]},
+ restart => transient,
+ shutdown => ?SUPERVISOR_WAIT,
+ type => supervisor,
+ modules => [rabbit_mqtt_retainer_sup]
+ }
+ | listener_specs(
+ fun tcp_listener_spec/1,
+ [SocketOpts, NumTcpAcceptors, ConcurrentConnsSups],
+ Listeners
+ ) ++
+ listener_specs(
+ fun ssl_listener_spec/1,
+ [SocketOpts, SslOpts, NumSslAcceptors, ConcurrentConnsSups],
+ SslListeners
+ )
+ ]
+ }}.
-spec stop_listeners() -> ok.
stop_listeners() ->
@@ -89,33 +98,33 @@ listener_specs(Fun, Args, Listeners) ->
tcp_listener_spec([Address, SocketOpts, NumAcceptors, ConcurrentConnsSups]) ->
rabbit_networking:tcp_listener_spec(
- rabbit_mqtt_listener_sup,
- Address,
- SocketOpts,
- transport(?TCP_PROTOCOL),
- rabbit_mqtt_reader,
- [],
- mqtt,
- NumAcceptors,
- ConcurrentConnsSups,
- worker,
- "MQTT TCP listener"
- ).
+ rabbit_mqtt_listener_sup,
+ Address,
+ SocketOpts,
+ transport(?TCP_PROTOCOL),
+ rabbit_mqtt_reader,
+ [],
+ mqtt,
+ NumAcceptors,
+ ConcurrentConnsSups,
+ worker,
+ "MQTT TCP listener"
+ ).
ssl_listener_spec([Address, SocketOpts, SslOpts, NumAcceptors, ConcurrentConnsSups]) ->
rabbit_networking:tcp_listener_spec(
- rabbit_mqtt_listener_sup,
- Address,
- SocketOpts ++ SslOpts,
- transport(?TLS_PROTOCOL),
- rabbit_mqtt_reader,
- [],
- 'mqtt/ssl',
- NumAcceptors,
- ConcurrentConnsSups,
- worker,
- "MQTT TLS listener"
- ).
+ rabbit_mqtt_listener_sup,
+ Address,
+ SocketOpts ++ SslOpts,
+ transport(?TLS_PROTOCOL),
+ rabbit_mqtt_reader,
+ [],
+ 'mqtt/ssl',
+ NumAcceptors,
+ ConcurrentConnsSups,
+ worker,
+ "MQTT TLS listener"
+ ).
transport(?TCP_PROTOCOL) ->
ranch_tcp;
diff --git a/deps/rabbitmq_mqtt/src/rabbit_mqtt_util.erl b/deps/rabbitmq_mqtt/src/rabbit_mqtt_util.erl
index d098d3ff93..89428c5965 100644
--- a/deps/rabbitmq_mqtt/src/rabbit_mqtt_util.erl
+++ b/deps/rabbitmq_mqtt/src/rabbit_mqtt_util.erl
@@ -11,20 +11,21 @@
-include("rabbit_mqtt.hrl").
-include("rabbit_mqtt_packet.hrl").
--export([queue_name_bin/2,
- qos_from_queue_name/2,
- env/1,
- table_lookup/2,
- path_for/2,
- path_for/3,
- vhost_name_to_table_name/1,
- register_clientid/2,
- remove_duplicate_clientid_connections/2,
- init_sparkplug/0,
- mqtt_to_amqp/1,
- amqp_to_mqtt/1,
- truncate_binary/2
- ]).
+-export([
+ queue_name_bin/2,
+ qos_from_queue_name/2,
+ env/1,
+ table_lookup/2,
+ path_for/2,
+ path_for/3,
+ vhost_name_to_table_name/1,
+ register_clientid/2,
+ remove_duplicate_clientid_connections/2,
+ init_sparkplug/0,
+ mqtt_to_amqp/1,
+ amqp_to_mqtt/1,
+ truncate_binary/2
+]).
-define(MAX_TOPIC_TRANSLATION_CACHE_SIZE, 12).
-define(SPARKPLUG_MP_MQTT_TO_AMQP, sparkplug_mp_mqtt_to_amqp).
@@ -72,7 +73,8 @@ init_sparkplug() ->
-spec mqtt_to_amqp(binary()) -> binary().
mqtt_to_amqp(Topic) ->
- T = case persistent_term:get(?SPARKPLUG_MP_MQTT_TO_AMQP, no_sparkplug) of
+ T =
+ case persistent_term:get(?SPARKPLUG_MP_MQTT_TO_AMQP, no_sparkplug) of
no_sparkplug ->
Topic;
M2A_SpRe ->
@@ -102,12 +104,13 @@ amqp_to_mqtt(Topic) ->
end.
cached(CacheName, Fun, Arg) ->
- Cache = case get(CacheName) of
- undefined ->
- [];
- Other ->
- Other
- end,
+ Cache =
+ case get(CacheName) of
+ undefined ->
+ [];
+ Other ->
+ Other
+ end,
case lists:keyfind(Arg, 1, Cache) of
{_, V} ->
V;
@@ -142,11 +145,11 @@ env(Key) ->
coerce_env_value(default_pass, Val) -> rabbit_data_coercion:to_binary(Val);
coerce_env_value(default_user, Val) -> rabbit_data_coercion:to_binary(Val);
-coerce_env_value(exchange, Val) -> rabbit_data_coercion:to_binary(Val);
-coerce_env_value(vhost, Val) -> rabbit_data_coercion:to_binary(Val);
-coerce_env_value(_, Val) -> Val.
+coerce_env_value(exchange, Val) -> rabbit_data_coercion:to_binary(Val);
+coerce_env_value(vhost, Val) -> rabbit_data_coercion:to_binary(Val);
+coerce_env_value(_, Val) -> Val.
--spec table_lookup(rabbit_framing:amqp_table() | undefined, binary()) ->
+-spec table_lookup(rabbit_framing:amqp_table() | undefined, binary()) ->
tuple() | undefined.
table_lookup(undefined, _Key) ->
undefined;
@@ -161,11 +164,11 @@ vhost_name_to_dir_name(VHost, Suffix) ->
-spec path_for(file:name_all(), rabbit_types:vhost()) -> file:filename_all().
path_for(Dir, VHost) ->
- filename:join(Dir, vhost_name_to_dir_name(VHost)).
+ filename:join(Dir, vhost_name_to_dir_name(VHost)).
-spec path_for(file:name_all(), rabbit_types:vhost(), string()) -> file:filename_all().
path_for(Dir, VHost, Suffix) ->
- filename:join(Dir, vhost_name_to_dir_name(VHost, Suffix)).
+ filename:join(Dir, vhost_name_to_dir_name(VHost, Suffix)).
-spec vhost_name_to_table_name(rabbit_types:vhost()) ->
atom().
@@ -174,8 +177,9 @@ vhost_name_to_table_name(VHost) ->
list_to_atom("rabbit_mqtt_retained_" ++ rabbit_misc:format("~36.16.0b", [Num])).
-spec register_clientid(rabbit_types:vhost(), binary()) -> ok.
-register_clientid(Vhost, ClientId)
- when is_binary(Vhost), is_binary(ClientId) ->
+register_clientid(Vhost, ClientId) when
+ is_binary(Vhost), is_binary(ClientId)
+->
PgGroup = {Vhost, ClientId},
ok = pg:join(persistent_term:get(?PG_SCOPE), PgGroup, self()),
case rabbit_mqtt_ff:track_client_id_in_ra() of
@@ -183,10 +187,12 @@ register_clientid(Vhost, ClientId)
%% Ra node takes care of removing duplicate client ID connections.
ok;
false ->
- ok = erpc:multicast([node() | nodes()],
- ?MODULE,
- remove_duplicate_clientid_connections,
- [PgGroup, self()])
+ ok = erpc:multicast(
+ [node() | nodes()],
+ ?MODULE,
+ remove_duplicate_clientid_connections,
+ [PgGroup, self()]
+ )
end.
-spec remove_duplicate_clientid_connections({rabbit_types:vhost(), binary()}, pid()) -> ok.
@@ -194,18 +200,24 @@ remove_duplicate_clientid_connections(PgGroup, PidToKeep) ->
try persistent_term:get(?PG_SCOPE) of
PgScope ->
Pids = pg:get_local_members(PgScope, PgGroup),
- lists:foreach(fun(Pid) ->
- gen_server:cast(Pid, duplicate_id)
- end, Pids -- [PidToKeep])
- catch _:badarg ->
- %% MQTT supervision tree on this node not fully started
- ok
+ lists:foreach(
+ fun(Pid) ->
+ gen_server:cast(Pid, duplicate_id)
+ end,
+ Pids -- [PidToKeep]
+ )
+ catch
+ _:badarg ->
+ %% MQTT supervision tree on this node not fully started
+ ok
end.
-spec truncate_binary(binary(), non_neg_integer()) -> binary().
-truncate_binary(Bin, Size)
- when is_binary(Bin) andalso byte_size(Bin) =< Size ->
+truncate_binary(Bin, Size) when
+ is_binary(Bin) andalso byte_size(Bin) =< Size
+->
Bin;
-truncate_binary(Bin, Size)
- when is_binary(Bin) ->
+truncate_binary(Bin, Size) when
+ is_binary(Bin)
+->
binary:part(Bin, 0, Size).
diff --git a/deps/rabbitmq_mqtt/test/auth_SUITE.erl b/deps/rabbitmq_mqtt/test/auth_SUITE.erl
index 63ac362842..f2ad72f857 100644
--- a/deps/rabbitmq_mqtt/test/auth_SUITE.erl
+++ b/deps/rabbitmq_mqtt/test/auth_SUITE.erl
@@ -5,8 +5,10 @@
%% Copyright (c) 2007-2023 VMware, Inc. or its affiliates. All rights reserved.
%%
-module(auth_SUITE).
--compile([export_all,
- nowarn_export_all]).
+-compile([
+ export_all,
+ nowarn_export_all
+]).
-include_lib("common_test/include/ct.hrl").
-include_lib("eunit/include/eunit.hrl").
@@ -14,79 +16,78 @@
%% defined in MQTT v4 and v5 (not in v3)
-define(SUBACK_FAILURE, 16#80).
--define(FAIL_IF_CRASH_LOG, {["Generic server.*terminating"],
- fun () -> ct:fail(crash_detected) end}).
+-define(FAIL_IF_CRASH_LOG, {["Generic server.*terminating"], fun() -> ct:fail(crash_detected) end}).
-import(rabbit_ct_broker_helpers, [rpc/5]).
all() ->
- [{group, anonymous_no_ssl_user},
- {group, anonymous_ssl_user},
- {group, no_ssl_user},
- {group, ssl_user},
- {group, client_id_propagation},
- {group, authz},
- {group, limit}].
+ [
+ {group, anonymous_no_ssl_user},
+ {group, anonymous_ssl_user},
+ {group, no_ssl_user},
+ {group, ssl_user},
+ {group, client_id_propagation},
+ {group, authz},
+ {group, limit}
+ ].
groups() ->
- [{anonymous_ssl_user, [],
- [anonymous_auth_success,
- user_credentials_auth,
- ssl_user_auth_success,
- ssl_user_vhost_not_allowed,
- ssl_user_vhost_parameter_mapping_success,
- ssl_user_vhost_parameter_mapping_not_allowed,
- ssl_user_vhost_parameter_mapping_vhost_does_not_exist,
- ssl_user_port_vhost_mapping_takes_precedence_over_cert_vhost_mapping
- ]},
- {anonymous_no_ssl_user, [],
- [anonymous_auth_success,
- user_credentials_auth,
- port_vhost_mapping_success,
- port_vhost_mapping_success_no_mapping,
- port_vhost_mapping_not_allowed,
- port_vhost_mapping_vhost_does_not_exist
- %% SSL auth will succeed, because we cannot ignore anonymous
- ]},
- {ssl_user, [],
- [anonymous_auth_failure,
- user_credentials_auth,
- ssl_user_auth_success,
- ssl_user_vhost_not_allowed,
- ssl_user_vhost_parameter_mapping_success,
- ssl_user_vhost_parameter_mapping_not_allowed,
- ssl_user_vhost_parameter_mapping_vhost_does_not_exist,
- ssl_user_port_vhost_mapping_takes_precedence_over_cert_vhost_mapping
- ]},
- {no_ssl_user, [],
- [anonymous_auth_failure,
- user_credentials_auth,
- ssl_user_auth_failure,
- port_vhost_mapping_success,
- port_vhost_mapping_success_no_mapping,
- port_vhost_mapping_not_allowed,
- port_vhost_mapping_vhost_does_not_exist
- ]},
- {client_id_propagation, [],
- [client_id_propagation]
- },
- {authz, [],
- [no_queue_bind_permission,
- no_queue_unbind_permission,
- no_queue_consume_permission,
- no_queue_consume_permission_on_connect,
- no_queue_delete_permission,
- no_queue_declare_permission,
- no_publish_permission,
- no_topic_read_permission,
- no_topic_write_permission,
- loopback_user_connects_from_remote_host
- ]
- },
- {limit, [],
- [vhost_connection_limit,
- vhost_queue_limit,
- user_connection_limit
- ]}
+ [
+ {anonymous_ssl_user, [], [
+ anonymous_auth_success,
+ user_credentials_auth,
+ ssl_user_auth_success,
+ ssl_user_vhost_not_allowed,
+ ssl_user_vhost_parameter_mapping_success,
+ ssl_user_vhost_parameter_mapping_not_allowed,
+ ssl_user_vhost_parameter_mapping_vhost_does_not_exist,
+ ssl_user_port_vhost_mapping_takes_precedence_over_cert_vhost_mapping
+ ]},
+ {anonymous_no_ssl_user, [], [
+ anonymous_auth_success,
+ user_credentials_auth,
+ port_vhost_mapping_success,
+ port_vhost_mapping_success_no_mapping,
+ port_vhost_mapping_not_allowed,
+ port_vhost_mapping_vhost_does_not_exist
+ %% SSL auth will succeed, because we cannot ignore anonymous
+ ]},
+ {ssl_user, [], [
+ anonymous_auth_failure,
+ user_credentials_auth,
+ ssl_user_auth_success,
+ ssl_user_vhost_not_allowed,
+ ssl_user_vhost_parameter_mapping_success,
+ ssl_user_vhost_parameter_mapping_not_allowed,
+ ssl_user_vhost_parameter_mapping_vhost_does_not_exist,
+ ssl_user_port_vhost_mapping_takes_precedence_over_cert_vhost_mapping
+ ]},
+ {no_ssl_user, [], [
+ anonymous_auth_failure,
+ user_credentials_auth,
+ ssl_user_auth_failure,
+ port_vhost_mapping_success,
+ port_vhost_mapping_success_no_mapping,
+ port_vhost_mapping_not_allowed,
+ port_vhost_mapping_vhost_does_not_exist
+ ]},
+ {client_id_propagation, [], [client_id_propagation]},
+ {authz, [], [
+ no_queue_bind_permission,
+ no_queue_unbind_permission,
+ no_queue_consume_permission,
+ no_queue_consume_permission_on_connect,
+ no_queue_delete_permission,
+ no_queue_declare_permission,
+ no_publish_permission,
+ no_topic_read_permission,
+ no_topic_write_permission,
+ loopback_user_connects_from_remote_host
+ ]},
+ {limit, [], [
+ vhost_connection_limit,
+ vhost_queue_limit,
+ user_connection_limit
+ ]}
].
init_per_suite(Config) ->
@@ -100,19 +101,29 @@ init_per_group(authz, Config0) ->
User = <<"mqtt-user">>,
Password = <<"mqtt-password">>,
VHost = <<"mqtt-vhost">>,
- MqttConfig = {rabbitmq_mqtt, [{default_user, User}
- ,{default_pass, Password}
- ,{allow_anonymous, true}
- ,{vhost, VHost}
- ,{exchange, <<"amq.topic">>}
- ]},
- Config1 = rabbit_ct_helpers:run_setup_steps(rabbit_ct_helpers:merge_app_env(Config0, MqttConfig),
- rabbit_ct_broker_helpers:setup_steps() ++
- rabbit_ct_client_helpers:setup_steps()),
+ MqttConfig =
+ {rabbitmq_mqtt, [
+ {default_user, User},
+ {default_pass, Password},
+ {allow_anonymous, true},
+ {vhost, VHost},
+ {exchange, <<"amq.topic">>}
+ ]},
+ Config1 = rabbit_ct_helpers:run_setup_steps(
+ rabbit_ct_helpers:merge_app_env(Config0, MqttConfig),
+ rabbit_ct_broker_helpers:setup_steps() ++
+ rabbit_ct_client_helpers:setup_steps()
+ ),
rabbit_ct_broker_helpers:add_user(Config1, User, Password),
rabbit_ct_broker_helpers:add_vhost(Config1, VHost),
- [Log|_] = rpc(Config1, 0, rabbit, log_locations, []),
- [{mqtt_user, User}, {mqtt_vhost, VHost}, {mqtt_password, Password}, {log_location, Log}|Config1];
+ [Log | _] = rpc(Config1, 0, rabbit, log_locations, []),
+ [
+ {mqtt_user, User},
+ {mqtt_vhost, VHost},
+ {mqtt_password, Password},
+ {log_location, Log}
+ | Config1
+ ];
init_per_group(Group, Config) ->
Suffix = rabbit_ct_helpers:testcase_absname(Config, "", "-"),
Config1 = rabbit_ct_helpers:set_config(Config, [
@@ -121,56 +132,77 @@ init_per_group(Group, Config) ->
]),
MqttConfig = mqtt_config(Group),
AuthConfig = auth_config(Group),
- rabbit_ct_helpers:run_setup_steps(Config1,
- [ fun(Conf) -> case MqttConfig of
- undefined -> Conf;
- _ -> merge_app_env(MqttConfig, Conf)
- end
- end] ++
- [ fun(Conf) -> case AuthConfig of
- undefined -> Conf;
- _ -> merge_app_env(AuthConfig, Conf)
- end
- end ] ++
- rabbit_ct_broker_helpers:setup_steps() ++
- rabbit_ct_client_helpers:setup_steps()).
+ rabbit_ct_helpers:run_setup_steps(
+ Config1,
+ [
+ fun(Conf) ->
+ case MqttConfig of
+ undefined -> Conf;
+ _ -> merge_app_env(MqttConfig, Conf)
+ end
+ end
+ ] ++
+ [
+ fun(Conf) ->
+ case AuthConfig of
+ undefined -> Conf;
+ _ -> merge_app_env(AuthConfig, Conf)
+ end
+ end
+ ] ++
+ rabbit_ct_broker_helpers:setup_steps() ++
+ rabbit_ct_client_helpers:setup_steps()
+ ).
end_per_group(_, Config) ->
- rabbit_ct_helpers:run_teardown_steps(Config,
- rabbit_ct_client_helpers:teardown_steps() ++
- rabbit_ct_broker_helpers:teardown_steps()).
+ rabbit_ct_helpers:run_teardown_steps(
+ Config,
+ rabbit_ct_client_helpers:teardown_steps() ++
+ rabbit_ct_broker_helpers:teardown_steps()
+ ).
merge_app_env(MqttConfig, Config) ->
rabbit_ct_helpers:merge_app_env(Config, MqttConfig).
mqtt_config(anonymous_ssl_user) ->
- {rabbitmq_mqtt, [{ssl_cert_login, true},
- {allow_anonymous, true}]};
+ {rabbitmq_mqtt, [
+ {ssl_cert_login, true},
+ {allow_anonymous, true}
+ ]};
mqtt_config(anonymous_no_ssl_user) ->
- {rabbitmq_mqtt, [{ssl_cert_login, false},
- {allow_anonymous, true}]};
+ {rabbitmq_mqtt, [
+ {ssl_cert_login, false},
+ {allow_anonymous, true}
+ ]};
mqtt_config(ssl_user) ->
- {rabbitmq_mqtt, [{ssl_cert_login, true},
- {allow_anonymous, false}]};
+ {rabbitmq_mqtt, [
+ {ssl_cert_login, true},
+ {allow_anonymous, false}
+ ]};
mqtt_config(no_ssl_user) ->
- {rabbitmq_mqtt, [{ssl_cert_login, false},
- {allow_anonymous, false}]};
+ {rabbitmq_mqtt, [
+ {ssl_cert_login, false},
+ {allow_anonymous, false}
+ ]};
mqtt_config(client_id_propagation) ->
- {rabbitmq_mqtt, [{ssl_cert_login, true},
- {allow_anonymous, true}]};
+ {rabbitmq_mqtt, [
+ {ssl_cert_login, true},
+ {allow_anonymous, true}
+ ]};
mqtt_config(_) ->
undefined.
auth_config(client_id_propagation) ->
{rabbit, [
- {auth_backends, [rabbit_auth_backend_mqtt_mock]}
- ]
- };
+ {auth_backends, [rabbit_auth_backend_mqtt_mock]}
+ ]};
auth_config(_) ->
undefined.
-init_per_testcase(Testcase, Config) when Testcase == ssl_user_auth_success;
- Testcase == ssl_user_auth_failure ->
+init_per_testcase(Testcase, Config) when
+ Testcase == ssl_user_auth_success;
+ Testcase == ssl_user_auth_failure
+->
Config1 = set_cert_user_on_default_vhost(Config),
rabbit_ct_helpers:testcase_started(Config1, Testcase);
init_per_testcase(ssl_user_vhost_parameter_mapping_success, Config) ->
@@ -191,8 +223,10 @@ init_per_testcase(user_credentials_auth, Config) ->
Pass = <<"new-user-pass">>,
ok = rabbit_ct_broker_helpers:add_user(Config, 0, User, Pass),
ok = rabbit_ct_broker_helpers:set_full_permissions(Config, User, <<"/">>),
- Config1 = rabbit_ct_helpers:set_config(Config, [{new_user, User},
- {new_user_pass, Pass}]),
+ Config1 = rabbit_ct_helpers:set_config(Config, [
+ {new_user, User},
+ {new_user_pass, Pass}
+ ]),
rabbit_ct_helpers:testcase_started(Config1, user_credentials_auth);
init_per_testcase(ssl_user_vhost_not_allowed, Config) ->
Config1 = set_cert_user_on_default_vhost(Config),
@@ -205,7 +239,9 @@ init_per_testcase(ssl_user_vhost_parameter_mapping_vhost_does_not_exist, Config)
Config2 = set_vhost_for_cert_user(Config1, User),
VhostForCertUser = ?config(temp_vhost_for_ssl_user, Config2),
ok = rabbit_ct_broker_helpers:delete_vhost(Config, VhostForCertUser),
- rabbit_ct_helpers:testcase_started(Config1, ssl_user_vhost_parameter_mapping_vhost_does_not_exist);
+ rabbit_ct_helpers:testcase_started(
+ Config1, ssl_user_vhost_parameter_mapping_vhost_does_not_exist
+ );
init_per_testcase(port_vhost_mapping_success, Config) ->
User = <<"guest">>,
Config1 = set_vhost_for_port_vhost_mapping_user(Config, User),
@@ -215,9 +251,12 @@ init_per_testcase(port_vhost_mapping_success_no_mapping, Config) ->
User = <<"guest">>,
Config1 = set_vhost_for_port_vhost_mapping_user(Config, User),
PortToVHostMappingParameter = [
- {<<"1">>, <<"unlikely to exist">>},
- {<<"2">>, <<"unlikely to exist">>}],
- ok = rabbit_ct_broker_helpers:set_global_parameter(Config, mqtt_port_to_vhost_mapping, PortToVHostMappingParameter),
+ {<<"1">>, <<"unlikely to exist">>},
+ {<<"2">>, <<"unlikely to exist">>}
+ ],
+ ok = rabbit_ct_broker_helpers:set_global_parameter(
+ Config, mqtt_port_to_vhost_mapping, PortToVHostMappingParameter
+ ),
VHost = ?config(temp_vhost_for_port_mapping, Config1),
rabbit_ct_broker_helpers:clear_permissions(Config1, User, VHost),
rabbit_ct_helpers:testcase_started(Config1, port_vhost_mapping_success_no_mapping);
@@ -245,7 +284,9 @@ init_per_testcase(ssl_user_port_vhost_mapping_takes_precedence_over_cert_vhost_m
rabbit_ct_broker_helpers:clear_permissions(Config3, User, VhostForPortMapping),
rabbit_ct_broker_helpers:clear_permissions(Config3, User, <<"/">>),
- rabbit_ct_helpers:testcase_started(Config3, ssl_user_port_vhost_mapping_takes_precedence_over_cert_vhost_mapping);
+ rabbit_ct_helpers:testcase_started(
+ Config3, ssl_user_port_vhost_mapping_takes_precedence_over_cert_vhost_mapping
+ );
init_per_testcase(Testcase, Config) ->
rabbit_ct_helpers:testcase_started(Config, Testcase).
@@ -268,7 +309,9 @@ set_vhost_for_cert_user(Config, User) ->
],
ok = rabbit_ct_broker_helpers:add_vhost(Config, VhostForCertUser),
ok = rabbit_ct_broker_helpers:set_full_permissions(Config, User, VhostForCertUser),
- ok = rabbit_ct_broker_helpers:set_global_parameter(Config, mqtt_default_vhosts, UserToVHostMappingParameter),
+ ok = rabbit_ct_broker_helpers:set_global_parameter(
+ Config, mqtt_default_vhosts, UserToVHostMappingParameter
+ ),
rabbit_ct_helpers:set_config(Config, [{temp_vhost_for_ssl_user, VhostForCertUser}]).
set_vhost_for_port_vhost_mapping_user(Config, User) ->
@@ -276,24 +319,29 @@ set_vhost_for_port_vhost_mapping_user(Config, User) ->
Port = rabbit_ct_broker_helpers:get_node_config(Config, 0, tcp_port_mqtt),
TlsPort = rabbit_ct_broker_helpers:get_node_config(Config, 0, tcp_port_mqtt_tls),
PortToVHostMappingParameter = [
- {integer_to_binary(Port), VhostForPortMapping},
- {<<"1884">>, <<"vhost2">>},
+ {integer_to_binary(Port), VhostForPortMapping},
+ {<<"1884">>, <<"vhost2">>},
{integer_to_binary(TlsPort), VhostForPortMapping},
- {<<"8884">>, <<"vhost2">>}
-
+ {<<"8884">>, <<"vhost2">>}
],
ok = rabbit_ct_broker_helpers:add_vhost(Config, VhostForPortMapping),
ok = rabbit_ct_broker_helpers:set_full_permissions(Config, User, VhostForPortMapping),
- ok = rabbit_ct_broker_helpers:set_global_parameter(Config, mqtt_port_to_vhost_mapping, PortToVHostMappingParameter),
+ ok = rabbit_ct_broker_helpers:set_global_parameter(
+ Config, mqtt_port_to_vhost_mapping, PortToVHostMappingParameter
+ ),
rabbit_ct_helpers:set_config(Config, [{temp_vhost_for_port_mapping, VhostForPortMapping}]).
-end_per_testcase(Testcase, Config) when Testcase == ssl_user_auth_success;
- Testcase == ssl_user_auth_failure;
- Testcase == ssl_user_vhost_not_allowed ->
+end_per_testcase(Testcase, Config) when
+ Testcase == ssl_user_auth_success;
+ Testcase == ssl_user_auth_failure;
+ Testcase == ssl_user_vhost_not_allowed
+->
delete_cert_user(Config),
rabbit_ct_helpers:testcase_finished(Config, Testcase);
-end_per_testcase(TestCase, Config) when TestCase == ssl_user_vhost_parameter_mapping_success;
- TestCase == ssl_user_vhost_parameter_mapping_not_allowed ->
+end_per_testcase(TestCase, Config) when
+ TestCase == ssl_user_vhost_parameter_mapping_success;
+ TestCase == ssl_user_vhost_parameter_mapping_not_allowed
+->
delete_cert_user(Config),
VhostForCertUser = ?config(temp_vhost_for_ssl_user, Config),
ok = rabbit_ct_broker_helpers:delete_vhost(Config, VhostForCertUser),
@@ -301,15 +349,19 @@ end_per_testcase(TestCase, Config) when TestCase == ssl_user_vhost_parameter_map
rabbit_ct_helpers:testcase_finished(Config, TestCase);
end_per_testcase(user_credentials_auth, Config) ->
User = ?config(new_user, Config),
- {ok,_} = rabbit_ct_broker_helpers:rabbitmqctl(Config, 0, ["delete_user", User]),
+ {ok, _} = rabbit_ct_broker_helpers:rabbitmqctl(Config, 0, ["delete_user", User]),
rabbit_ct_helpers:testcase_finished(Config, user_credentials_auth);
end_per_testcase(ssl_user_vhost_parameter_mapping_vhost_does_not_exist, Config) ->
delete_cert_user(Config),
ok = rabbit_ct_broker_helpers:clear_global_parameter(Config, mqtt_default_vhosts),
- rabbit_ct_helpers:testcase_finished(Config, ssl_user_vhost_parameter_mapping_vhost_does_not_exist);
-end_per_testcase(Testcase, Config) when Testcase == port_vhost_mapping_success;
- Testcase == port_vhost_mapping_not_allowed;
- Testcase == port_vhost_mapping_success_no_mapping ->
+ rabbit_ct_helpers:testcase_finished(
+ Config, ssl_user_vhost_parameter_mapping_vhost_does_not_exist
+ );
+end_per_testcase(Testcase, Config) when
+ Testcase == port_vhost_mapping_success;
+ Testcase == port_vhost_mapping_not_allowed;
+ Testcase == port_vhost_mapping_success_no_mapping
+->
User = <<"guest">>,
rabbit_ct_broker_helpers:set_full_permissions(Config, User, <<"/">>),
VHost = ?config(temp_vhost_for_port_mapping, Config),
@@ -330,24 +382,31 @@ end_per_testcase(ssl_user_port_vhost_mapping_takes_precedence_over_cert_vhost_ma
VHostForPortVHostMapping = ?config(temp_vhost_for_port_mapping, Config),
ok = rabbit_ct_broker_helpers:delete_vhost(Config, VHostForPortVHostMapping),
ok = rabbit_ct_broker_helpers:clear_global_parameter(Config, mqtt_port_to_vhost_mapping),
- rabbit_ct_helpers:testcase_finished(Config, ssl_user_port_vhost_mapping_takes_precedence_over_cert_vhost_mapping);
-end_per_testcase(Testcase, Config) when Testcase == no_queue_bind_permission;
- Testcase == no_queue_unbind_permission;
- Testcase == no_queue_consume_permission;
- Testcase == no_queue_consume_permission_on_connect;
- Testcase == no_queue_delete_permission;
- Testcase == no_queue_declare_permission;
- Testcase == no_publish_permission;
- Testcase == no_topic_read_permission;
- Testcase == no_topic_write_permission;
- Testcase == loopback_user_connects_from_remote_host ->
+ rabbit_ct_helpers:testcase_finished(
+ Config, ssl_user_port_vhost_mapping_takes_precedence_over_cert_vhost_mapping
+ );
+end_per_testcase(Testcase, Config) when
+ Testcase == no_queue_bind_permission;
+ Testcase == no_queue_unbind_permission;
+ Testcase == no_queue_consume_permission;
+ Testcase == no_queue_consume_permission_on_connect;
+ Testcase == no_queue_delete_permission;
+ Testcase == no_queue_declare_permission;
+ Testcase == no_publish_permission;
+ Testcase == no_topic_read_permission;
+ Testcase == no_topic_write_permission;
+ Testcase == loopback_user_connects_from_remote_host
+->
%% So let's wait before logs are surely flushed
Marker = "MQTT_AUTH_SUITE_MARKER",
rpc(Config, 0, rabbit_log, error, [Marker]),
- wait_log(Config, [{[Marker], fun () -> stop end}]),
+ wait_log(Config, [{[Marker], fun() -> stop end}]),
%% Preserve file contents in case some investigation is needed, before truncating.
- file:copy(?config(log_location, Config), iolist_to_binary([?config(log_location, Config), ".", atom_to_binary(Testcase)])),
+ file:copy(
+ ?config(log_location, Config),
+ iolist_to_binary([?config(log_location, Config), ".", atom_to_binary(Testcase)])
+ ),
%% And provide an empty log file for the next test in this group
file:write_file(?config(log_location, Config), <<>>),
@@ -358,7 +417,7 @@ end_per_testcase(Testcase, Config) ->
delete_cert_user(Config) ->
User = ?config(temp_ssl_user, Config),
- {ok,_} = rabbit_ct_broker_helpers:rabbitmqctl(Config, 0, ["delete_user", User]).
+ {ok, _} = rabbit_ct_broker_helpers:rabbitmqctl(Config, 0, ["delete_user", User]).
anonymous_auth_success(Config) ->
expect_successful_connection(fun connect_anonymous/1, Config).
@@ -366,7 +425,6 @@ anonymous_auth_success(Config) ->
anonymous_auth_failure(Config) ->
expect_authentication_failure(fun connect_anonymous/1, Config).
-
ssl_user_auth_success(Config) ->
expect_successful_connection(fun connect_ssl/1, Config).
@@ -379,31 +437,38 @@ user_credentials_auth(Config) ->
expect_successful_connection(
fun(Conf) -> connect_user(NewUser, NewUserPass, Conf) end,
- Config),
+ Config
+ ),
expect_successful_connection(
fun(Conf) -> connect_user(<<"guest">>, <<"guest">>, Conf) end,
- Config),
+ Config
+ ),
expect_successful_connection(
fun(Conf) -> connect_user(<<"/:guest">>, <<"guest">>, Conf) end,
- Config),
+ Config
+ ),
expect_authentication_failure(
fun(Conf) -> connect_user(NewUser, <<"invalid_pass">>, Conf) end,
- Config),
+ Config
+ ),
expect_authentication_failure(
fun(Conf) -> connect_user(undefined, <<"pass">>, Conf) end,
- Config),
+ Config
+ ),
expect_authentication_failure(
fun(Conf) -> connect_user(NewUser, undefined, Conf) end,
- Config),
+ Config
+ ),
expect_authentication_failure(
fun(Conf) -> connect_user(<<"non-existing-vhost:guest">>, <<"guest">>, Conf) end,
- Config).
+ Config
+ ).
ssl_user_vhost_parameter_mapping_success(Config) ->
expect_successful_connection(fun connect_ssl/1, Config).
@@ -420,7 +485,8 @@ ssl_user_vhost_parameter_mapping_vhost_does_not_exist(Config) ->
port_vhost_mapping_success(Config) ->
expect_successful_connection(
fun(Conf) -> connect_user(<<"guest">>, <<"guest">>, Conf) end,
- Config).
+ Config
+ ).
port_vhost_mapping_success_no_mapping(Config) ->
%% no vhost mapping for the port, falling back to default vhost
@@ -450,68 +516,92 @@ connect_anonymous(Config) ->
connect_anonymous(Config, ClientId) ->
P = rabbit_ct_broker_helpers:get_node_config(Config, 0, tcp_port_mqtt),
- emqtt:start_link([{host, "localhost"},
- {port, P},
- {clientid, ClientId},
- {proto_ver, v4}]).
+ emqtt:start_link([
+ {host, "localhost"},
+ {port, P},
+ {clientid, ClientId},
+ {proto_ver, v4}
+ ]).
connect_ssl(Config) ->
CertsDir = ?config(rmq_certsdir, Config),
- SSLConfig = [{cacertfile, filename:join([CertsDir, "testca", "cacert.pem"])},
- {certfile, filename:join([CertsDir, "client", "cert.pem"])},
- {keyfile, filename:join([CertsDir, "client", "key.pem"])}],
+ SSLConfig = [
+ {cacertfile, filename:join([CertsDir, "testca", "cacert.pem"])},
+ {certfile, filename:join([CertsDir, "client", "cert.pem"])},
+ {keyfile, filename:join([CertsDir, "client", "key.pem"])}
+ ],
P = rabbit_ct_broker_helpers:get_node_config(Config, 0, tcp_port_mqtt_tls),
- emqtt:start_link([{host, "localhost"},
- {port, P},
- {clientid, <<"simpleClient">>},
- {proto_ver, v4},
- {ssl, true},
- {ssl_opts, SSLConfig}]).
+ emqtt:start_link([
+ {host, "localhost"},
+ {port, P},
+ {clientid, <<"simpleClient">>},
+ {proto_ver, v4},
+ {ssl, true},
+ {ssl_opts, SSLConfig}
+ ]).
client_id_propagation(Config) ->
- ok = rabbit_ct_broker_helpers:add_code_path_to_all_nodes(Config,
- rabbit_auth_backend_mqtt_mock),
+ ok = rabbit_ct_broker_helpers:add_code_path_to_all_nodes(
+ Config,
+ rabbit_auth_backend_mqtt_mock
+ ),
%% setup creates the ETS table required for the mqtt auth mock
%% it blocks indefinitely so we need to spawn
Self = self(),
_ = spawn(
- fun () ->
- rpc(Config, 0, rabbit_auth_backend_mqtt_mock, setup, [Self])
- end),
+ fun() ->
+ rpc(Config, 0, rabbit_auth_backend_mqtt_mock, setup, [Self])
+ end
+ ),
%% the setup process will notify us
receive
ok -> ok
- after
- 3000 -> ct:fail("timeout waiting for rabbit_auth_backend_mqtt_mock:setup/1")
+ after 3000 -> ct:fail("timeout waiting for rabbit_auth_backend_mqtt_mock:setup/1")
end,
ClientId = <<"client-id-propagation">>,
- {ok, C} = connect_user(<<"fake-user">>, <<"fake-password">>,
- Config, ClientId),
+ {ok, C} = connect_user(
+ <<"fake-user">>,
+ <<"fake-password">>,
+ Config,
+ ClientId
+ ),
{ok, _} = emqtt:connect(C),
{ok, _, _} = emqtt:subscribe(C, <<"TopicA">>),
- [{authentication, AuthProps}] = rpc(Config, 0,
- rabbit_auth_backend_mqtt_mock,
- get,
- [authentication]),
+ [{authentication, AuthProps}] = rpc(
+ Config,
+ 0,
+ rabbit_auth_backend_mqtt_mock,
+ get,
+ [authentication]
+ ),
?assertEqual(ClientId, proplists:get_value(client_id, AuthProps)),
- [{vhost_access, AuthzData}] = rpc(Config, 0,
- rabbit_auth_backend_mqtt_mock,
- get,
- [vhost_access]),
+ [{vhost_access, AuthzData}] = rpc(
+ Config,
+ 0,
+ rabbit_auth_backend_mqtt_mock,
+ get,
+ [vhost_access]
+ ),
?assertEqual(ClientId, maps:get(<<"client_id">>, AuthzData)),
- [{resource_access, AuthzContext}] = rpc(Config, 0,
- rabbit_auth_backend_mqtt_mock,
- get,
- [resource_access]),
+ [{resource_access, AuthzContext}] = rpc(
+ Config,
+ 0,
+ rabbit_auth_backend_mqtt_mock,
+ get,
+ [resource_access]
+ ),
?assertEqual(true, maps:size(AuthzContext) > 0),
?assertEqual(ClientId, maps:get(<<"client_id">>, AuthzContext)),
- [{topic_access, TopicContext}] = rpc(Config, 0,
- rabbit_auth_backend_mqtt_mock,
- get,
- [topic_access]),
+ [{topic_access, TopicContext}] = rpc(
+ Config,
+ 0,
+ rabbit_auth_backend_mqtt_mock,
+ get,
+ [topic_access]
+ ),
VariableMap = maps:get(variable_map, TopicContext),
?assertEqual(ClientId, maps:get(<<"client_id">>, VariableMap)),
@@ -531,12 +621,13 @@ client_id_propagation(Config) ->
%% flushed, and won't contaminate following tests from this group.
no_queue_bind_permission(Config) ->
ExpectedLogs =
- ["MQTT resource access refused: write access to queue "
- "'mqtt-subscription-mqtt-userqos0' in vhost 'mqtt-vhost' "
- "refused for user 'mqtt-user'",
- "Failed to bind queue 'mqtt-subscription-mqtt-userqos0' "
- "in vhost 'mqtt-vhost' with topic test/topic: access_refused"
- ],
+ [
+ "MQTT resource access refused: write access to queue "
+ "'mqtt-subscription-mqtt-userqos0' in vhost 'mqtt-vhost' "
+ "refused for user 'mqtt-user'",
+ "Failed to bind queue 'mqtt-subscription-mqtt-userqos0' "
+ "in vhost 'mqtt-vhost' with topic test/topic: access_refused"
+ ],
test_subscribe_permissions_combination(<<".*">>, <<"">>, <<".*">>, Config, ExpectedLogs).
no_queue_unbind_permission(Config) ->
@@ -544,36 +635,45 @@ no_queue_unbind_permission(Config) ->
Vhost = ?config(mqtt_vhost, Config),
rabbit_ct_broker_helpers:set_permissions(Config, User, Vhost, <<".*">>, <<".*">>, <<".*">>),
P = rabbit_ct_broker_helpers:get_node_config(Config, 0, tcp_port_mqtt),
- Opts = [{host, "localhost"},
- {port, P},
- {proto_ver, v4},
- {clientid, User},
- {username, User},
- {password, ?config(mqtt_password, Config)}],
+ Opts = [
+ {host, "localhost"},
+ {port, P},
+ {proto_ver, v4},
+ {clientid, User},
+ {username, User},
+ {password, ?config(mqtt_password, Config)}
+ ],
{ok, C1} = emqtt:start_link([{clean_start, false} | Opts]),
{ok, _} = emqtt:connect(C1),
Topic = <<"my/topic">>,
- ?assertMatch({ok, _Properties, [1]},
- emqtt:subscribe(C1, Topic, qos1)),
+ ?assertMatch(
+ {ok, _Properties, [1]},
+ emqtt:subscribe(C1, Topic, qos1)
+ ),
ok = emqtt:disconnect(C1),
%% Revoke read access to amq.topic exchange.
- rabbit_ct_broker_helpers:set_permissions(Config, User, Vhost, <<".*">>, <<".*">>, <<"^(?!amq\.topic$)">>),
+ rabbit_ct_broker_helpers:set_permissions(
+ Config, User, Vhost, <<".*">>, <<".*">>, <<"^(?!amq\.topic$)">>
+ ),
{ok, C2} = emqtt:start_link([{clean_start, false} | Opts]),
{ok, _} = emqtt:connect(C2),
process_flag(trap_exit, true),
%% We subscribe with the same client ID to the same topic again, but this time with QoS 0.
%% Therefore we trigger the qos1 queue to be unbound (and the qos0 queue to be bound).
%% However, unbinding requires read access to the exchange, which we don't have anymore.
- ?assertMatch({ok, _Properties, [?SUBACK_FAILURE]},
- emqtt:subscribe(C2, Topic, qos0)),
+ ?assertMatch(
+ {ok, _Properties, [?SUBACK_FAILURE]},
+ emqtt:subscribe(C2, Topic, qos0)
+ ),
ok = assert_connection_closed(C2),
ExpectedLogs =
- ["MQTT resource access refused: read access to exchange 'amq.topic' in vhost 'mqtt-vhost' refused for user 'mqtt-user'",
- "Failed to unbind queue 'mqtt-subscription-mqtt-userqos1' in vhost 'mqtt-vhost' with topic 'my/topic': access_refused",
- "MQTT protocol error on connection.*: subscribe_error"
- ],
- wait_log(Config, [?FAIL_IF_CRASH_LOG, {ExpectedLogs, fun () -> stop end}]),
+ [
+ "MQTT resource access refused: read access to exchange 'amq.topic' in vhost 'mqtt-vhost' refused for user 'mqtt-user'",
+ "Failed to unbind queue 'mqtt-subscription-mqtt-userqos1' in vhost 'mqtt-vhost' with topic 'my/topic': access_refused",
+ "MQTT protocol error on connection.*: subscribe_error"
+ ],
+ wait_log(Config, [?FAIL_IF_CRASH_LOG, {ExpectedLogs, fun() -> stop end}]),
%% Clean up the qos1 queue by connecting with clean session.
rabbit_ct_broker_helpers:set_permissions(Config, User, Vhost, <<".*">>, <<".*">>, <<".*">>),
@@ -583,20 +683,25 @@ no_queue_unbind_permission(Config) ->
no_queue_consume_permission(Config) ->
ExpectedLogs =
- ["MQTT resource access refused: read access to queue "
- "'mqtt-subscription-mqtt-userqos0' in vhost 'mqtt-vhost' "
- "refused for user 'mqtt-user'"],
- test_subscribe_permissions_combination(<<".*">>, <<".*">>, <<"^amq\\.topic">>, Config, ExpectedLogs).
+ [
+ "MQTT resource access refused: read access to queue "
+ "'mqtt-subscription-mqtt-userqos0' in vhost 'mqtt-vhost' "
+ "refused for user 'mqtt-user'"
+ ],
+ test_subscribe_permissions_combination(
+ <<".*">>, <<".*">>, <<"^amq\\.topic">>, Config, ExpectedLogs
+ ).
no_queue_delete_permission(Config) ->
set_permissions(".*", ".*", ".*", Config),
ClientId = <<"no_queue_delete_permission">>,
{ok, C1} = connect_user(
- ?config(mqtt_user, Config),
- ?config(mqtt_password, Config),
- Config,
- ClientId,
- [{clean_start, false}]),
+ ?config(mqtt_user, Config),
+ ?config(mqtt_password, Config),
+ Config,
+ ClientId,
+ [{clean_start, false}]
+ ),
{ok, _} = emqtt:connect(C1),
{ok, _, _} = emqtt:subscribe(C1, {<<"test/topic">>, qos1}),
ok = emqtt:disconnect(C1),
@@ -605,82 +710,114 @@ no_queue_delete_permission(Config) ->
%% Now we have a durable queue that user doesn't have permission to delete.
%% Attempt to establish clean session should fail.
{ok, C2} = connect_user(
- ?config(mqtt_user, Config),
- ?config(mqtt_password, Config),
- Config,
- ClientId,
- [{clean_start, true}]),
+ ?config(mqtt_user, Config),
+ ?config(mqtt_password, Config),
+ Config,
+ ClientId,
+ [{clean_start, true}]
+ ),
unlink(C2),
- ?assertMatch({error, _},
- emqtt:connect(C2)),
+ ?assertMatch(
+ {error, _},
+ emqtt:connect(C2)
+ ),
wait_log(
- Config,
- [?FAIL_IF_CRASH_LOG
- ,{[io_lib:format("MQTT resource access refused: configure access to queue "
+ Config,
+ [
+ ?FAIL_IF_CRASH_LOG,
+ {
+ [
+ io_lib:format(
+ "MQTT resource access refused: configure access to queue "
"'mqtt-subscription-~sqos1' in vhost 'mqtt-vhost' refused for user 'mqtt-user'",
- [ClientId]),
- "MQTT connection .* is closing due to an authorization failure"],
- fun() -> stop end}
- ]),
+ [ClientId]
+ ),
+ "MQTT connection .* is closing due to an authorization failure"
+ ],
+ fun() -> stop end
+ }
+ ]
+ ),
ok.
no_queue_consume_permission_on_connect(Config) ->
set_permissions(".*", ".*", ".*", Config),
ClientId = <<"no_queue_consume_permission_on_connect">>,
{ok, C1} = connect_user(
- ?config(mqtt_user, Config),
- ?config(mqtt_password, Config),
- Config,
- ClientId,
- [{clean_start, false}]),
+ ?config(mqtt_user, Config),
+ ?config(mqtt_password, Config),
+ Config,
+ ClientId,
+ [{clean_start, false}]
+ ),
{ok, _} = emqtt:connect(C1),
{ok, _, _} = emqtt:subscribe(C1, {<<"test/topic">>, qos1}),
ok = emqtt:disconnect(C1),
set_permissions(".*", ".*", "^amq\\.topic", Config),
{ok, C2} = connect_user(
- ?config(mqtt_user, Config),
- ?config(mqtt_password, Config),
- Config,
- ClientId,
- [{clean_start, false}]),
+ ?config(mqtt_user, Config),
+ ?config(mqtt_password, Config),
+ Config,
+ ClientId,
+ [{clean_start, false}]
+ ),
unlink(C2),
- ?assertMatch({error, _},
- emqtt:connect(C2)),
+ ?assertMatch(
+ {error, _},
+ emqtt:connect(C2)
+ ),
wait_log(
- Config,
- [?FAIL_IF_CRASH_LOG
- ,{[io_lib:format("MQTT resource access refused: read access to queue "
+ Config,
+ [
+ ?FAIL_IF_CRASH_LOG,
+ {
+ [
+ io_lib:format(
+ "MQTT resource access refused: read access to queue "
"'mqtt-subscription-~sqos1' in vhost 'mqtt-vhost' refused for user 'mqtt-user'",
- [ClientId]),
- "MQTT connection .* is closing due to an authorization failure"],
- fun () -> stop end}
- ]),
+ [ClientId]
+ ),
+ "MQTT connection .* is closing due to an authorization failure"
+ ],
+ fun() -> stop end
+ }
+ ]
+ ),
ok.
no_queue_declare_permission(Config) ->
set_permissions("", ".*", ".*", Config),
ClientId = <<"no_queue_declare_permission">>,
{ok, C} = connect_user(
- ?config(mqtt_user, Config),
- ?config(mqtt_password, Config),
- Config,
- ClientId,
- [{clean_start, true}]),
+ ?config(mqtt_user, Config),
+ ?config(mqtt_password, Config),
+ Config,
+ ClientId,
+ [{clean_start, true}]
+ ),
{ok, _} = emqtt:connect(C),
process_flag(trap_exit, true),
{ok, _, [?SUBACK_FAILURE]} = emqtt:subscribe(C, <<"test/topic">>, qos0),
ok = assert_connection_closed(C),
wait_log(
- Config,
- [?FAIL_IF_CRASH_LOG
- ,{[io_lib:format("MQTT resource access refused: configure access to queue "
+ Config,
+ [
+ ?FAIL_IF_CRASH_LOG,
+ {
+ [
+ io_lib:format(
+ "MQTT resource access refused: configure access to queue "
"'mqtt-subscription-~sqos0' in vhost 'mqtt-vhost' refused for user 'mqtt-user'",
- [ClientId]),
- "MQTT protocol error on connection .*: subscribe_error"],
- fun () -> stop end}
- ]),
+ [ClientId]
+ ),
+ "MQTT protocol error on connection .*: subscribe_error"
+ ],
+ fun() -> stop end
+ }
+ ]
+ ),
ok.
no_publish_permission(Config) ->
@@ -689,13 +826,20 @@ no_publish_permission(Config) ->
process_flag(trap_exit, true),
ok = emqtt:publish(C, <<"some/topic">>, <<"payload">>),
assert_connection_closed(C),
- wait_log(Config,
- [?FAIL_IF_CRASH_LOG
- ,{["MQTT resource access refused: write access to exchange "
- "'amq.topic' in vhost 'mqtt-vhost' refused for user 'mqtt-user'",
- "MQTT connection .* is closing due to an authorization failure"],
- fun () -> stop end}
- ]),
+ wait_log(
+ Config,
+ [
+ ?FAIL_IF_CRASH_LOG,
+ {
+ [
+ "MQTT resource access refused: write access to exchange "
+ "'amq.topic' in vhost 'mqtt-vhost' refused for user 'mqtt-user'",
+ "MQTT connection .* is closing due to an authorization failure"
+ ],
+ fun() -> stop end
+ }
+ ]
+ ),
ok.
no_topic_read_permission(Config) ->
@@ -709,15 +853,21 @@ no_topic_read_permission(Config) ->
process_flag(trap_exit, true),
{ok, _, [?SUBACK_FAILURE]} = emqtt:subscribe(C, <<"test/topic">>),
ok = assert_connection_closed(C),
- wait_log(Config,
- [?FAIL_IF_CRASH_LOG,
- {["MQTT topic access refused: read access to topic 'test.topic' in exchange "
- "'amq.topic' in vhost 'mqtt-vhost' refused for user 'mqtt-user'",
- "Failed to bind queue 'mqtt-subscription-mqtt-userqos0' "
- "in vhost 'mqtt-vhost' with topic test/topic: access_refused"
- ],
- fun () -> stop end}
- ]),
+ wait_log(
+ Config,
+ [
+ ?FAIL_IF_CRASH_LOG,
+ {
+ [
+ "MQTT topic access refused: read access to topic 'test.topic' in exchange "
+ "'amq.topic' in vhost 'mqtt-vhost' refused for user 'mqtt-user'",
+ "Failed to bind queue 'mqtt-subscription-mqtt-userqos0' "
+ "in vhost 'mqtt-vhost' with topic test/topic: access_refused"
+ ],
+ fun() -> stop end
+ }
+ ]
+ ),
ok.
no_topic_write_permission(Config) ->
@@ -729,15 +879,24 @@ no_topic_write_permission(Config) ->
{ok, _} = emqtt:publish(C, <<"allow-write/some/topic">>, <<"payload">>, qos1),
process_flag(trap_exit, true),
- ?assertMatch({error, _},
- emqtt:publish(C, <<"some/other/topic">>, <<"payload">>, qos1)),
- wait_log(Config,
- [?FAIL_IF_CRASH_LOG
- ,{["MQTT topic access refused: write access to topic 'some.other.topic' in "
- "exchange 'amq.topic' in vhost 'mqtt-vhost' refused for user 'mqtt-user'",
- "MQTT connection .* is closing due to an authorization failure"],
- fun () -> stop end}
- ]),
+ ?assertMatch(
+ {error, _},
+ emqtt:publish(C, <<"some/other/topic">>, <<"payload">>, qos1)
+ ),
+ wait_log(
+ Config,
+ [
+ ?FAIL_IF_CRASH_LOG,
+ {
+ [
+ "MQTT topic access refused: write access to topic 'some.other.topic' in "
+ "exchange 'amq.topic' in vhost 'mqtt-vhost' refused for user 'mqtt-user'",
+ "MQTT connection .* is closing due to an authorization failure"
+ ],
+ fun() -> stop end
+ }
+ ]
+ ),
ok.
loopback_user_connects_from_remote_host(Config) ->
@@ -754,65 +913,107 @@ loopback_user_connects_from_remote_host(Config) ->
process_flag(trap_exit, true),
?assertMatch({error, _}, emqtt:connect(C)),
- wait_log(Config,
- [?FAIL_IF_CRASH_LOG,
- {["MQTT login failed: user 'mqtt-user' can only connect via localhost",
- "MQTT connection .* is closing due to an authorization failure"],
- fun () -> stop end}
- ]),
+ wait_log(
+ Config,
+ [
+ ?FAIL_IF_CRASH_LOG,
+ {
+ [
+ "MQTT login failed: user 'mqtt-user' can only connect via localhost",
+ "MQTT connection .* is closing due to an authorization failure"
+ ],
+ fun() -> stop end
+ }
+ ]
+ ),
true = rpc(Config, 0, meck, validate, [Mod]),
ok = rpc(Config, 0, meck, unload, [Mod]).
set_topic_permissions(WritePat, ReadPat, Config) ->
- rpc(Config, 0,
- rabbit_auth_backend_internal, set_topic_permissions,
- [?config(mqtt_user, Config), ?config(mqtt_vhost, Config),
- <<"amq.topic">>, WritePat, ReadPat, <<"acting-user">>]).
+ rpc(
+ Config,
+ 0,
+ rabbit_auth_backend_internal,
+ set_topic_permissions,
+ [
+ ?config(mqtt_user, Config),
+ ?config(mqtt_vhost, Config),
+ <<"amq.topic">>,
+ WritePat,
+ ReadPat,
+ <<"acting-user">>
+ ]
+ ).
set_permissions(PermConf, PermWrite, PermRead, Config) ->
- rabbit_ct_broker_helpers:set_permissions(Config, ?config(mqtt_user, Config), ?config(mqtt_vhost, Config),
- iolist_to_binary(PermConf),
- iolist_to_binary(PermWrite),
- iolist_to_binary(PermRead)).
+ rabbit_ct_broker_helpers:set_permissions(
+ Config,
+ ?config(mqtt_user, Config),
+ ?config(mqtt_vhost, Config),
+ iolist_to_binary(PermConf),
+ iolist_to_binary(PermWrite),
+ iolist_to_binary(PermRead)
+ ).
open_mqtt_connection(Config) ->
open_mqtt_connection(Config, []).
open_mqtt_connection(Config, Opts) ->
- {ok, C} = connect_user(?config(mqtt_user, Config), ?config(mqtt_password, Config), Config, ?config(mqtt_user, Config), Opts),
+ {ok, C} = connect_user(
+ ?config(mqtt_user, Config),
+ ?config(mqtt_password, Config),
+ Config,
+ ?config(mqtt_user, Config),
+ Opts
+ ),
{ok, _} = emqtt:connect(C),
C.
test_subscribe_permissions_combination(PermConf, PermWrite, PermRead, Config, ExtraLogChecks) ->
- rabbit_ct_broker_helpers:set_permissions(Config,
- ?config(mqtt_user, Config),
- ?config(mqtt_vhost, Config),
- PermConf, PermWrite, PermRead),
+ rabbit_ct_broker_helpers:set_permissions(
+ Config,
+ ?config(mqtt_user, Config),
+ ?config(mqtt_vhost, Config),
+ PermConf,
+ PermWrite,
+ PermRead
+ ),
P = rabbit_ct_broker_helpers:get_node_config(Config, 0, tcp_port_mqtt),
User = ?config(mqtt_user, Config),
- Opts = [{host, "localhost"},
- {port, P},
- {clientid, User},
- {username, User},
- {password, ?config(mqtt_password, Config)}],
+ Opts = [
+ {host, "localhost"},
+ {port, P},
+ {clientid, User},
+ {username, User},
+ {password, ?config(mqtt_password, Config)}
+ ],
{ok, C1} = emqtt:start_link([{proto_ver, v4} | Opts]),
{ok, _} = emqtt:connect(C1),
process_flag(trap_exit, true),
%% In v4, we expect to receive a failure return code for our subscription in the SUBACK packet.
- ?assertMatch({ok, _Properties, [?SUBACK_FAILURE]},
- emqtt:subscribe(C1, <<"test/topic">>)),
+ ?assertMatch(
+ {ok, _Properties, [?SUBACK_FAILURE]},
+ emqtt:subscribe(C1, <<"test/topic">>)
+ ),
ok = assert_connection_closed(C1),
- wait_log(Config,
- [?FAIL_IF_CRASH_LOG
- ,{["MQTT protocol error on connection.*: subscribe_error"|ExtraLogChecks], fun () -> stop end}
- ]),
+ wait_log(
+ Config,
+ [
+ ?FAIL_IF_CRASH_LOG,
+ {["MQTT protocol error on connection.*: subscribe_error" | ExtraLogChecks], fun() ->
+ stop
+ end}
+ ]
+ ),
{ok, C2} = emqtt:start_link([{proto_ver, v3} | Opts]),
{ok, _} = emqtt:connect(C2),
%% In v3, there is no failure return code in the SUBACK packet.
- ?assertMatch({ok, _Properties, [0]},
- emqtt:subscribe(C2, <<"test/topic">>)),
+ ?assertMatch(
+ {ok, _Properties, [0]},
+ emqtt:subscribe(C2, <<"test/topic">>)
+ ),
ok = assert_connection_closed(C2).
connect_user(User, Pass, Config) ->
@@ -820,20 +1021,25 @@ connect_user(User, Pass, Config) ->
connect_user(User, Pass, Config, ClientID) ->
connect_user(User, Pass, Config, ClientID, []).
connect_user(User, Pass, Config, ClientID0, Opts) ->
- Creds = case User of
- undefined -> [];
- _ -> [{username, User}]
- end ++ case Pass of
- undefined -> [];
- _ -> [{password, Pass}]
- end,
- ClientID = case ClientID0 of
- undefined -> [];
- ID -> [{clientid, ID}]
- end,
+ Creds =
+ case User of
+ undefined -> [];
+ _ -> [{username, User}]
+ end ++
+ case Pass of
+ undefined -> [];
+ _ -> [{password, Pass}]
+ end,
+ ClientID =
+ case ClientID0 of
+ undefined -> [];
+ ID -> [{clientid, ID}]
+ end,
P = rabbit_ct_broker_helpers:get_node_config(Config, 0, tcp_port_mqtt),
- emqtt:start_link(Opts ++ Creds ++ ClientID ++
- [{host, "localhost"}, {port, P}, {proto_ver, v4}]).
+ emqtt:start_link(
+ Opts ++ Creds ++ ClientID ++
+ [{host, "localhost"}, {port, P}, {proto_ver, v4}]
+ ).
expect_successful_connection(ConnectFun, Config) ->
rpc(Config, 0, rabbit_core_metrics, reset_auth_attempt_metrics, []),
@@ -881,10 +1087,14 @@ vhost_queue_limit(Config) ->
process_flag(trap_exit, true),
%% qos0 queue can be created, qos1 queue fails to be created.
%% (RabbitMQ creates subscriptions in the reverse order of the SUBSCRIBE packet.)
- ?assertMatch({ok, _Properties, [?SUBACK_FAILURE, ?SUBACK_FAILURE, 0]},
- emqtt:subscribe(C, [{<<"topic1">>, qos1},
- {<<"topic2">>, qos1},
- {<<"topic3">>, qos0}])),
+ ?assertMatch(
+ {ok, _Properties, [?SUBACK_FAILURE, ?SUBACK_FAILURE, 0]},
+ emqtt:subscribe(C, [
+ {<<"topic1">>, qos1},
+ {<<"topic2">>, qos1},
+ {<<"topic3">>, qos0}
+ ])
+ ),
ok = assert_connection_closed(C).
user_connection_limit(Config) ->
@@ -906,42 +1116,47 @@ wait_log(Config, Clauses, Deadline) ->
case erlang:monotonic_time(millisecond) of
T when T =< Deadline ->
case wait_log_check_clauses(Content, Clauses) of
- stop -> ok;
+ stop ->
+ ok;
continue ->
timer:sleep(50),
wait_log(Config, Clauses, Deadline)
end;
_ ->
lists:foreach(
- fun
- ({REs, _}) ->
- Matches = [ io_lib:format("~p - ~s~n", [RE, re:run(Content, RE, [{capture, none}])]) || RE <- REs ],
- ct:pal("Wait log clause status: ~s", [Matches])
- end, Clauses),
+ fun({REs, _}) ->
+ Matches = [
+ io_lib:format("~p - ~s~n", [RE, re:run(Content, RE, [{capture, none}])])
+ || RE <- REs
+ ],
+ ct:pal("Wait log clause status: ~s", [Matches])
+ end,
+ Clauses
+ ),
ct:fail(expected_logs_not_found)
end,
ok.
wait_log_check_clauses(_, []) ->
continue;
-wait_log_check_clauses(Content, [{REs, Fun}|Rest]) ->
+wait_log_check_clauses(Content, [{REs, Fun} | Rest]) ->
case multiple_re_match(Content, REs) of
true -> Fun();
- _ ->
- wait_log_check_clauses(Content, Rest)
+ _ -> wait_log_check_clauses(Content, Rest)
end.
multiple_re_match(Content, REs) ->
lists:all(
- fun (RE) ->
- match == re:run(Content, RE, [{capture, none}])
- end, REs).
+ fun(RE) ->
+ match == re:run(Content, RE, [{capture, none}])
+ end,
+ REs
+ ).
assert_connection_closed(ClientPid) ->
receive
{'EXIT', ClientPid, {shutdown, tcp_closed}} ->
ok
- after
- 2000 ->
- ct:fail("timed out waiting for exit message")
+ after 2000 ->
+ ct:fail("timed out waiting for exit message")
end.
diff --git a/deps/rabbitmq_mqtt/test/cluster_SUITE.erl b/deps/rabbitmq_mqtt/test/cluster_SUITE.erl
index 0ac2434ea2..15f4e92ddc 100644
--- a/deps/rabbitmq_mqtt/test/cluster_SUITE.erl
+++ b/deps/rabbitmq_mqtt/test/cluster_SUITE.erl
@@ -9,36 +9,42 @@
-include_lib("common_test/include/ct.hrl").
-include_lib("eunit/include/eunit.hrl").
--import(util, [expect_publishes/3,
- connect/3,
- connect/4,
- await_exit/1]).
-
--import(rabbit_ct_broker_helpers,
- [setup_steps/0,
- teardown_steps/0,
- get_node_config/3,
- rabbitmqctl/3,
- rpc/4,
- stop_node/2
- ]).
-
--define(OPTS, [{connect_timeout, 1},
- {ack_timeout, 1}]).
+-import(util, [
+ expect_publishes/3,
+ connect/3,
+ connect/4,
+ await_exit/1
+]).
+
+-import(
+ rabbit_ct_broker_helpers,
+ [
+ setup_steps/0,
+ teardown_steps/0,
+ get_node_config/3,
+ rabbitmqctl/3,
+ rpc/4,
+ stop_node/2
+ ]
+).
+
+-define(OPTS, [
+ {connect_timeout, 1},
+ {ack_timeout, 1}
+]).
all() ->
[
- {group, cluster_size_5}
+ {group, cluster_size_5}
].
groups() ->
[
- {cluster_size_5, [],
- [
- connection_id_tracking,
- connection_id_tracking_on_nodedown,
- connection_id_tracking_with_decommissioned_node
- ]}
+ {cluster_size_5, [], [
+ connection_id_tracking,
+ connection_id_tracking_on_nodedown,
+ connection_id_tracking_with_decommissioned_node
+ ]}
].
suite() ->
@@ -50,11 +56,12 @@ suite() ->
merge_app_env(Config) ->
rabbit_ct_helpers:merge_app_env(
- Config,
- {rabbit, [
- {collect_statistics, basic},
- {collect_statistics_interval, 100}
- ]}).
+ Config,
+ {rabbit, [
+ {collect_statistics, basic},
+ {collect_statistics_interval, 100}
+ ]}
+ ).
init_per_suite(Config) ->
rabbit_ct_helpers:log_environment(),
@@ -65,7 +72,8 @@ end_per_suite(Config) ->
init_per_group(cluster_size_5, Config) ->
rabbit_ct_helpers:set_config(
- Config, [{rmq_nodes_count, 5}]).
+ Config, [{rmq_nodes_count, 5}]
+ ).
end_per_group(_, Config) ->
Config.
@@ -75,19 +83,25 @@ init_per_testcase(Testcase, Config) ->
rabbit_ct_helpers:log_environment(),
Config1 = rabbit_ct_helpers:set_config(Config, [
{rmq_nodename_suffix, Testcase},
- {rmq_extra_tcp_ports, [tcp_port_mqtt_extra,
- tcp_port_mqtt_tls_extra]},
+ {rmq_extra_tcp_ports, [
+ tcp_port_mqtt_extra,
+ tcp_port_mqtt_tls_extra
+ ]},
{rmq_nodes_clustered, true}
- ]),
- rabbit_ct_helpers:run_setup_steps(Config1,
- [ fun merge_app_env/1 ] ++
- setup_steps() ++
- rabbit_ct_client_helpers:setup_steps()).
+ ]),
+ rabbit_ct_helpers:run_setup_steps(
+ Config1,
+ [fun merge_app_env/1] ++
+ setup_steps() ++
+ rabbit_ct_client_helpers:setup_steps()
+ ).
end_per_testcase(Testcase, Config) ->
- rabbit_ct_helpers:run_teardown_steps(Config,
- rabbit_ct_client_helpers:teardown_steps() ++
- teardown_steps()),
+ rabbit_ct_helpers:run_teardown_steps(
+ Config,
+ rabbit_ct_client_helpers:teardown_steps() ++
+ teardown_steps()
+ ),
rabbit_ct_helpers:testcase_finished(Config, Testcase).
%% -------------------------------------------------------------------
@@ -165,14 +179,15 @@ connection_id_tracking_with_decommissioned_node(Config) ->
%% Helpers
%%
-assert_connection_count(_Config, 0, _, NumElements) ->
+assert_connection_count(_Config, 0, _, NumElements) ->
ct:fail("failed to match connection count ~b", [NumElements]);
assert_connection_count(Config, Retries, NodeId, NumElements) ->
case util:all_connection_pids(Config) of
- Pids
- when length(Pids) =:= NumElements ->
+ Pids when
+ length(Pids) =:= NumElements
+ ->
ok;
_ ->
timer:sleep(500),
- assert_connection_count(Config, Retries-1, NodeId, NumElements)
+ assert_connection_count(Config, Retries - 1, NodeId, NumElements)
end.
diff --git a/deps/rabbitmq_mqtt/test/command_SUITE.erl b/deps/rabbitmq_mqtt/test/command_SUITE.erl
index 76421287d3..1f3831f94e 100644
--- a/deps/rabbitmq_mqtt/test/command_SUITE.erl
+++ b/deps/rabbitmq_mqtt/test/command_SUITE.erl
@@ -4,7 +4,6 @@
%%
%% Copyright (c) 2007-2023 VMware, Inc. or its affiliates. All rights reserved.
-
-module(command_SUITE).
-compile([export_all, nowarn_export_all]).
@@ -17,39 +16,45 @@
all() ->
[
- {group, non_parallel_tests}
+ {group, non_parallel_tests}
].
groups() ->
[
- {non_parallel_tests, [], [
- merge_defaults,
- run
- ]}
+ {non_parallel_tests, [], [
+ merge_defaults,
+ run
+ ]}
].
suite() ->
[
- {timetrap, {minutes, 3}}
+ {timetrap, {minutes, 3}}
].
init_per_suite(Config) ->
rabbit_ct_helpers:log_environment(),
Config1 = rabbit_ct_helpers:set_config(Config, [
{rmq_nodename_suffix, ?MODULE},
- {rmq_extra_tcp_ports, [tcp_port_mqtt_extra,
- tcp_port_mqtt_tls_extra]},
+ {rmq_extra_tcp_ports, [
+ tcp_port_mqtt_extra,
+ tcp_port_mqtt_tls_extra
+ ]},
{rmq_nodes_clustered, true},
{rmq_nodes_count, 3}
- ]),
- rabbit_ct_helpers:run_setup_steps(Config1,
- rabbit_ct_broker_helpers:setup_steps() ++
- rabbit_ct_client_helpers:setup_steps()).
+ ]),
+ rabbit_ct_helpers:run_setup_steps(
+ Config1,
+ rabbit_ct_broker_helpers:setup_steps() ++
+ rabbit_ct_client_helpers:setup_steps()
+ ).
end_per_suite(Config) ->
- rabbit_ct_helpers:run_teardown_steps(Config,
- rabbit_ct_client_helpers:teardown_steps() ++
- rabbit_ct_broker_helpers:teardown_steps()).
+ rabbit_ct_helpers:run_teardown_steps(
+ Config,
+ rabbit_ct_client_helpers:teardown_steps() ++
+ rabbit_ct_broker_helpers:teardown_steps()
+ ).
init_per_group(_, Config) ->
Config.
@@ -73,7 +78,6 @@ merge_defaults(_Config) ->
{[<<"other_key">>], #{verbose := false}} =
?COMMAND:merge_defaults([<<"other_key">>], #{verbose => false}).
-
run(Config) ->
Node = rabbit_ct_broker_helpers:get_node_config(Config, 0, nodename),
Opts = #{node => Node, timeout => 10_000, verbose => false},
@@ -91,18 +95,27 @@ run(Config) ->
C2 = connect(<<"simpleClient1">>, Config, [{ack_timeout, 1}]),
timer:sleep(200),
- [[{client_id, <<"simpleClient">>}, {user, <<"guest">>}],
- [{client_id, <<"simpleClient1">>}, {user, <<"guest">>}]] =
+ [
+ [{client_id, <<"simpleClient">>}, {user, <<"guest">>}],
+ [{client_id, <<"simpleClient1">>}, {user, <<"guest">>}]
+ ] =
lists:sort(
- 'Elixir.Enum':to_list(?COMMAND:run([<<"client_id">>, <<"user">>],
- Opts))),
+ 'Elixir.Enum':to_list(
+ ?COMMAND:run(
+ [<<"client_id">>, <<"user">>],
+ Opts
+ )
+ )
+ ),
Port = rabbit_ct_broker_helpers:get_node_config(Config, 0, tcp_port_amqp),
start_amqp_connection(network, Node, Port),
%% There are still just two MQTT connections
- [[{client_id, <<"simpleClient">>}],
- [{client_id, <<"simpleClient1">>}]] =
+ [
+ [{client_id, <<"simpleClient">>}],
+ [{client_id, <<"simpleClient1">>}]
+ ] =
lists:sort('Elixir.Enum':to_list(?COMMAND:run([<<"client_id">>], Opts))),
start_amqp_connection(direct, Node, Port),
@@ -110,14 +123,19 @@ run(Config) ->
%% Still two MQTT connections
?assertEqual(
- [[{client_id, <<"simpleClient">>}],
- [{client_id, <<"simpleClient1">>}]],
- lists:sort('Elixir.Enum':to_list(?COMMAND:run([<<"client_id">>], Opts)))),
+ [
+ [{client_id, <<"simpleClient">>}],
+ [{client_id, <<"simpleClient1">>}]
+ ],
+ lists:sort('Elixir.Enum':to_list(?COMMAND:run([<<"client_id">>], Opts)))
+ ),
%% Verbose returns all keys
AllKeys = lists:map(fun(I) -> atom_to_binary(I) end, ?INFO_ITEMS),
[AllInfos1Con1, _AllInfos1Con2] = 'Elixir.Enum':to_list(?COMMAND:run(AllKeys, Opts)),
- [AllInfos2Con1, _AllInfos2Con2] = 'Elixir.Enum':to_list(?COMMAND:run([], Opts#{verbose => true})),
+ [AllInfos2Con1, _AllInfos2Con2] = 'Elixir.Enum':to_list(
+ ?COMMAND:run([], Opts#{verbose => true})
+ ),
%% Keys are INFO_ITEMS
InfoItemsSorted = lists:sort(?INFO_ITEMS),
diff --git a/deps/rabbitmq_mqtt/test/config_SUITE.erl b/deps/rabbitmq_mqtt/test/config_SUITE.erl
index b2b04b52e2..37cb23f43a 100644
--- a/deps/rabbitmq_mqtt/test/config_SUITE.erl
+++ b/deps/rabbitmq_mqtt/test/config_SUITE.erl
@@ -5,8 +5,10 @@
%% Copyright (c) 2007-2023 VMware, Inc. or its affiliates. All rights reserved.
-module(config_SUITE).
--compile([export_all,
- nowarn_export_all]).
+-compile([
+ export_all,
+ nowarn_export_all
+]).
-include_lib("eunit/include/eunit.hrl").
@@ -14,17 +16,16 @@
all() ->
[
- {group, mnesia}
+ {group, mnesia}
].
groups() ->
[
- {mnesia, [shuffle],
- [
- rabbitmq_default,
- environment_set,
- flag_set
- ]}
+ {mnesia, [shuffle], [
+ rabbitmq_default,
+ environment_set,
+ flag_set
+ ]}
].
suite() ->
@@ -45,8 +46,12 @@ init_per_testcase(rabbitmq_default = Test, Config) ->
init_per_testcase0(Test, Config);
init_per_testcase(environment_set = Test, Config0) ->
Config = rabbit_ct_helpers:merge_app_env(
- Config0, {mnesia, [{dump_log_write_threshold, 25000},
- {dump_log_time_threshold, 60000}]}),
+ Config0,
+ {mnesia, [
+ {dump_log_write_threshold, 25000},
+ {dump_log_time_threshold, 60000}
+ ]}
+ ),
init_per_testcase0(Test, Config);
init_per_testcase(flag_set = Test, Config0) ->
Config = [{additional_erl_args, "-mnesia dump_log_write_threshold 15000"} | Config0],
@@ -55,17 +60,19 @@ init_per_testcase(flag_set = Test, Config0) ->
init_per_testcase0(Testcase, Config0) ->
Config1 = rabbit_ct_helpers:set_config(Config0, {rmq_nodename_suffix, Testcase}),
Config = rabbit_ct_helpers:run_steps(
- Config1,
- rabbit_ct_broker_helpers:setup_steps() ++
- rabbit_ct_client_helpers:setup_steps()),
+ Config1,
+ rabbit_ct_broker_helpers:setup_steps() ++
+ rabbit_ct_client_helpers:setup_steps()
+ ),
rabbit_ct_helpers:testcase_started(Config, Testcase).
end_per_testcase(Testcase, Config0) ->
Config = rabbit_ct_helpers:testcase_finished(Config0, Testcase),
rabbit_ct_helpers:run_teardown_steps(
- Config,
- rabbit_ct_client_helpers:teardown_steps() ++
- rabbit_ct_broker_helpers:teardown_steps()).
+ Config,
+ rabbit_ct_client_helpers:teardown_steps() ++
+ rabbit_ct_broker_helpers:teardown_steps()
+ ).
%% -------------------------------------------------------------------
%% Testsuite cases
@@ -74,21 +81,33 @@ end_per_testcase(Testcase, Config0) ->
%% The MQTT plugin expects Mnesia dump_log_write_threshold to be increased
%% from 1000 (Mnesia default) to 5000 (RabbitMQ default).
rabbitmq_default(Config) ->
- ?assertEqual(5_000,
- rpc(Config, 0, mnesia, system_info, [dump_log_write_threshold])),
- ?assertEqual(90_000,
- rpc(Config, 0, mnesia, system_info, [dump_log_time_threshold])).
+ ?assertEqual(
+ 5_000,
+ rpc(Config, 0, mnesia, system_info, [dump_log_write_threshold])
+ ),
+ ?assertEqual(
+ 90_000,
+ rpc(Config, 0, mnesia, system_info, [dump_log_time_threshold])
+ ).
%% User configured setting in advanced.config should be respected.
environment_set(Config) ->
- ?assertEqual(25_000,
- rpc(Config, 0, mnesia, system_info, [dump_log_write_threshold])),
- ?assertEqual(60_000,
- rpc(Config, 0, mnesia, system_info, [dump_log_time_threshold])).
+ ?assertEqual(
+ 25_000,
+ rpc(Config, 0, mnesia, system_info, [dump_log_write_threshold])
+ ),
+ ?assertEqual(
+ 60_000,
+ rpc(Config, 0, mnesia, system_info, [dump_log_time_threshold])
+ ).
%% User configured setting in RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS should be respected.
flag_set(Config) ->
- ?assertEqual(15_000,
- rpc(Config, 0, mnesia, system_info, [dump_log_write_threshold])),
- ?assertEqual(90_000,
- rpc(Config, 0, mnesia, system_info, [dump_log_time_threshold])).
+ ?assertEqual(
+ 15_000,
+ rpc(Config, 0, mnesia, system_info, [dump_log_write_threshold])
+ ),
+ ?assertEqual(
+ 90_000,
+ rpc(Config, 0, mnesia, system_info, [dump_log_time_threshold])
+ ).
diff --git a/deps/rabbitmq_mqtt/test/config_schema_SUITE.erl b/deps/rabbitmq_mqtt/test/config_schema_SUITE.erl
index c3f3d867c4..b62ca26185 100644
--- a/deps/rabbitmq_mqtt/test/config_schema_SUITE.erl
+++ b/deps/rabbitmq_mqtt/test/config_schema_SUITE.erl
@@ -23,7 +23,6 @@ init_per_suite(Config) ->
Config1 = rabbit_ct_helpers:run_setup_steps(Config),
rabbit_ct_config_schema:init_schemas(rabbitmq_mqtt, Config1).
-
end_per_suite(Config) ->
rabbit_ct_helpers:run_teardown_steps(Config).
@@ -31,15 +30,19 @@ init_per_testcase(Testcase, Config) ->
rabbit_ct_helpers:testcase_started(Config, Testcase),
Config1 = rabbit_ct_helpers:set_config(Config, [
{rmq_nodename_suffix, Testcase}
- ]),
- rabbit_ct_helpers:run_steps(Config1,
- rabbit_ct_broker_helpers:setup_steps() ++
- rabbit_ct_client_helpers:setup_steps()).
+ ]),
+ rabbit_ct_helpers:run_steps(
+ Config1,
+ rabbit_ct_broker_helpers:setup_steps() ++
+ rabbit_ct_client_helpers:setup_steps()
+ ).
end_per_testcase(Testcase, Config) ->
- Config1 = rabbit_ct_helpers:run_steps(Config,
- rabbit_ct_client_helpers:teardown_steps() ++
- rabbit_ct_broker_helpers:teardown_steps()),
+ Config1 = rabbit_ct_helpers:run_steps(
+ Config,
+ rabbit_ct_client_helpers:teardown_steps() ++
+ rabbit_ct_broker_helpers:teardown_steps()
+ ),
rabbit_ct_helpers:testcase_finished(Config1, Testcase).
%% -------------------------------------------------------------------
@@ -47,9 +50,13 @@ end_per_testcase(Testcase, Config) ->
%% -------------------------------------------------------------------
run_snippets(Config) ->
- ok = rabbit_ct_broker_helpers:rpc(Config, 0,
- ?MODULE, run_snippets1, [Config]).
+ ok = rabbit_ct_broker_helpers:rpc(
+ Config,
+ 0,
+ ?MODULE,
+ run_snippets1,
+ [Config]
+ ).
run_snippets1(Config) ->
rabbit_ct_config_schema:run_snippets(Config).
-
diff --git a/deps/rabbitmq_mqtt/test/event_recorder.erl b/deps/rabbitmq_mqtt/test/event_recorder.erl
index cd495f9427..4c2af35437 100644
--- a/deps/rabbitmq_mqtt/test/event_recorder.erl
+++ b/deps/rabbitmq_mqtt/test/event_recorder.erl
@@ -15,10 +15,11 @@
init(_) ->
{ok, ?INIT_STATE}.
-handle_event(#event{type = T}, State)
- when T =:= node_stats orelse
- T =:= node_node_stats orelse
- T =:= node_node_deleted ->
+handle_event(#event{type = T}, State) when
+ T =:= node_stats orelse
+ T =:= node_node_stats orelse
+ T =:= node_node_deleted
+->
{ok, State};
handle_event(Event, State) ->
{ok, [Event | State]}.
diff --git a/deps/rabbitmq_mqtt/test/ff_SUITE.erl b/deps/rabbitmq_mqtt/test/ff_SUITE.erl
index a7c528c640..5e002f808f 100644
--- a/deps/rabbitmq_mqtt/test/ff_SUITE.erl
+++ b/deps/rabbitmq_mqtt/test/ff_SUITE.erl
@@ -13,27 +13,31 @@
-import(rabbit_ct_broker_helpers, [rpc/5]).
-import(rabbit_ct_helpers, [eventually/1]).
--import(util, [expect_publishes/3,
- get_global_counters/4,
- connect/2,
- connect/4]).
+-import(util, [
+ expect_publishes/3,
+ get_global_counters/4,
+ connect/2,
+ connect/4
+]).
-define(PROTO_VER, v4).
all() ->
[
- {group, cluster_size_3}
+ {group, cluster_size_3}
].
groups() ->
[
- {cluster_size_3, [], [delete_ra_cluster_mqtt_node,
- rabbit_mqtt_qos0_queue]}
+ {cluster_size_3, [], [
+ delete_ra_cluster_mqtt_node,
+ rabbit_mqtt_qos0_queue
+ ]}
].
suite() ->
[
- {timetrap, {minutes, 2}}
+ {timetrap, {minutes, 2}}
].
init_per_suite(Config) ->
@@ -44,26 +48,36 @@ end_per_suite(Config) ->
rabbit_ct_helpers:run_teardown_steps(Config).
init_per_group(Group = cluster_size_3, Config0) ->
- Config1 = rabbit_ct_helpers:set_config(Config0, [{rmq_nodes_count, 3},
- {rmq_nodename_suffix, Group}]),
+ Config1 = rabbit_ct_helpers:set_config(Config0, [
+ {rmq_nodes_count, 3},
+ {rmq_nodename_suffix, Group}
+ ]),
Config = rabbit_ct_helpers:merge_app_env(
- Config1, {rabbit, [{forced_feature_flags_on_init, []}]}),
- rabbit_ct_helpers:run_steps(Config,
- rabbit_ct_broker_helpers:setup_steps() ++
- rabbit_ct_client_helpers:setup_steps()).
+ Config1, {rabbit, [{forced_feature_flags_on_init, []}]}
+ ),
+ rabbit_ct_helpers:run_steps(
+ Config,
+ rabbit_ct_broker_helpers:setup_steps() ++
+ rabbit_ct_client_helpers:setup_steps()
+ ).
end_per_group(_Group, Config) ->
- rabbit_ct_helpers:run_steps(Config,
- rabbit_ct_client_helpers:teardown_steps() ++
- rabbit_ct_broker_helpers:teardown_steps()).
+ rabbit_ct_helpers:run_steps(
+ Config,
+ rabbit_ct_client_helpers:teardown_steps() ++
+ rabbit_ct_broker_helpers:teardown_steps()
+ ).
init_per_testcase(TestCase, Config) ->
case rabbit_ct_broker_helpers:is_feature_flag_supported(Config, TestCase) of
true ->
Config;
false ->
- {skip, io_lib:format("feature flag ~s is unsupported",
- [TestCase])}
+ {skip,
+ io_lib:format(
+ "feature flag ~s is unsupported",
+ [TestCase]
+ )}
end.
end_per_testcase(_TestCase, Config) ->
@@ -76,16 +90,27 @@ delete_ra_cluster_mqtt_node(Config) ->
%% old client ID tracking works
?assertEqual(1, length(util:all_connection_pids(Config))),
%% Ra processes are alive
- ?assert(lists:all(fun erlang:is_pid/1,
- rabbit_ct_broker_helpers:rpc_all(Config, erlang, whereis, [mqtt_node]))),
-
- ?assertEqual(ok,
- rabbit_ct_broker_helpers:enable_feature_flag(Config, FeatureFlag)),
+ ?assert(
+ lists:all(
+ fun erlang:is_pid/1,
+ rabbit_ct_broker_helpers:rpc_all(Config, erlang, whereis, [mqtt_node])
+ )
+ ),
+
+ ?assertEqual(
+ ok,
+ rabbit_ct_broker_helpers:enable_feature_flag(Config, FeatureFlag)
+ ),
%% Ra processes should be gone
rabbit_ct_helpers:eventually(
- ?_assert(lists:all(fun(Pid) -> Pid =:= undefined end,
- rabbit_ct_broker_helpers:rpc_all(Config, erlang, whereis, [mqtt_node])))),
+ ?_assert(
+ lists:all(
+ fun(Pid) -> Pid =:= undefined end,
+ rabbit_ct_broker_helpers:rpc_all(Config, erlang, whereis, [mqtt_node])
+ )
+ )
+ ),
%% new client ID tracking works
?assertEqual(1, length(util:all_connection_pids(Config))),
?assert(erlang:is_process_alive(C)),
@@ -99,20 +124,30 @@ rabbit_mqtt_qos0_queue(Config) ->
{ok, _, [0]} = emqtt:subscribe(C1, Topic, qos0),
ok = emqtt:publish(C1, Topic, Msg, qos0),
ok = expect_publishes(C1, Topic, [Msg]),
- ?assertEqual(1,
- length(rpc(Config, 0, rabbit_amqqueue, list_by_type, [rabbit_classic_queue]))),
+ ?assertEqual(
+ 1,
+ length(rpc(Config, 0, rabbit_amqqueue, list_by_type, [rabbit_classic_queue]))
+ ),
- ?assertEqual(ok,
- rabbit_ct_broker_helpers:enable_feature_flag(Config, FeatureFlag)),
+ ?assertEqual(
+ ok,
+ rabbit_ct_broker_helpers:enable_feature_flag(Config, FeatureFlag)
+ ),
%% Queue type does not chanage for existing connection.
- ?assertEqual(1,
- length(rpc(Config, 0, rabbit_amqqueue, list_by_type, [rabbit_classic_queue]))),
+ ?assertEqual(
+ 1,
+ length(rpc(Config, 0, rabbit_amqqueue, list_by_type, [rabbit_classic_queue]))
+ ),
ok = emqtt:publish(C1, Topic, Msg, qos0),
ok = expect_publishes(C1, Topic, [Msg]),
- ?assertMatch(#{messages_delivered_total := 2,
- messages_delivered_consume_auto_ack_total := 2},
- get_global_counters(Config, ?PROTO_VER, 0, [{queue_type, rabbit_classic_queue}])),
+ ?assertMatch(
+ #{
+ messages_delivered_total := 2,
+ messages_delivered_consume_auto_ack_total := 2
+ },
+ get_global_counters(Config, ?PROTO_VER, 0, [{queue_type, rabbit_classic_queue}])
+ ),
%% Reconnecting with the same client ID will terminate the old connection.
true = unlink(C1),
@@ -120,13 +155,22 @@ rabbit_mqtt_qos0_queue(Config) ->
{ok, _, [0]} = emqtt:subscribe(C2, Topic, qos0),
%% This time, we get the new queue type.
eventually(
- ?_assertEqual(0,
- length(rpc(Config, 0, rabbit_amqqueue, list_by_type, [rabbit_classic_queue])))),
- ?assertEqual(1,
- length(rpc(Config, 0, rabbit_amqqueue, list_by_type, [FeatureFlag]))),
+ ?_assertEqual(
+ 0,
+ length(rpc(Config, 0, rabbit_amqqueue, list_by_type, [rabbit_classic_queue]))
+ )
+ ),
+ ?assertEqual(
+ 1,
+ length(rpc(Config, 0, rabbit_amqqueue, list_by_type, [FeatureFlag]))
+ ),
ok = emqtt:publish(C2, Topic, Msg, qos0),
ok = expect_publishes(C2, Topic, [Msg]),
- ?assertMatch(#{messages_delivered_total := 1,
- messages_delivered_consume_auto_ack_total := 1},
- get_global_counters(Config, ?PROTO_VER, 0, [{queue_type, FeatureFlag}])),
+ ?assertMatch(
+ #{
+ messages_delivered_total := 1,
+ messages_delivered_consume_auto_ack_total := 1
+ },
+ get_global_counters(Config, ?PROTO_VER, 0, [{queue_type, FeatureFlag}])
+ ),
ok = emqtt:disconnect(C2).
diff --git a/deps/rabbitmq_mqtt/test/java_SUITE.erl b/deps/rabbitmq_mqtt/test/java_SUITE.erl
index 5155f1ba77..4e3fdeed29 100644
--- a/deps/rabbitmq_mqtt/test/java_SUITE.erl
+++ b/deps/rabbitmq_mqtt/test/java_SUITE.erl
@@ -12,24 +12,25 @@
-include_lib("eunit/include/eunit.hrl").
-define(BASE_CONF_MQTT,
- {rabbitmq_mqtt, [
- {ssl_cert_login, true},
- {allow_anonymous, false},
- {sparkplug, true},
- {tcp_listeners, []},
- {ssl_listeners, []}
- ]}).
+ {rabbitmq_mqtt, [
+ {ssl_cert_login, true},
+ {allow_anonymous, false},
+ {sparkplug, true},
+ {tcp_listeners, []},
+ {ssl_listeners, []}
+ ]}
+).
all() ->
[
- {group, non_parallel_tests}
+ {group, non_parallel_tests}
].
groups() ->
[
- {non_parallel_tests, [], [
- java
- ]}
+ {non_parallel_tests, [], [
+ java
+ ]}
].
suite() ->
@@ -52,16 +53,20 @@ init_per_suite(Config) ->
{rmq_certspwd, "bunnychow"},
{rmq_nodes_clustered, true},
{rmq_nodes_count, 3}
- ]),
- rabbit_ct_helpers:run_setup_steps(Config1,
- [ fun merge_app_env/1 ] ++
- rabbit_ct_broker_helpers:setup_steps() ++
- rabbit_ct_client_helpers:setup_steps()).
+ ]),
+ rabbit_ct_helpers:run_setup_steps(
+ Config1,
+ [fun merge_app_env/1] ++
+ rabbit_ct_broker_helpers:setup_steps() ++
+ rabbit_ct_client_helpers:setup_steps()
+ ).
end_per_suite(Config) ->
- rabbit_ct_helpers:run_teardown_steps(Config,
- rabbit_ct_client_helpers:teardown_steps() ++
- rabbit_ct_broker_helpers:teardown_steps()).
+ rabbit_ct_helpers:run_teardown_steps(
+ Config,
+ rabbit_ct_client_helpers:teardown_steps() ++
+ rabbit_ct_broker_helpers:teardown_steps()
+ ).
init_per_group(_, Config) ->
Config.
@@ -74,25 +79,38 @@ init_per_testcase(Testcase, Config) ->
CertFile = filename:join([CertsDir, "client", "cert.pem"]),
{ok, CertBin} = file:read_file(CertFile),
[{'Certificate', Cert, not_encrypted}] = public_key:pem_decode(CertBin),
- UserBin = rabbit_ct_broker_helpers:rpc(Config, 0,
- rabbit_ssl,
- peer_cert_auth_name,
- [Cert]),
+ UserBin = rabbit_ct_broker_helpers:rpc(
+ Config,
+ 0,
+ rabbit_ssl,
+ peer_cert_auth_name,
+ [Cert]
+ ),
User = binary_to_list(UserBin),
- {ok,_} = rabbit_ct_broker_helpers:rabbitmqctl(Config, 0, ["add_user", User, ""]),
- {ok, _} = rabbit_ct_broker_helpers:rabbitmqctl(Config, 0, ["set_permissions", "-p", "/", User, ".*", ".*", ".*"]),
- {ok, _} = rabbit_ct_broker_helpers:rabbitmqctl(Config, 0,
- ["set_topic_permissions", "-p", "/", "guest", "amq.topic",
+ {ok, _} = rabbit_ct_broker_helpers:rabbitmqctl(Config, 0, ["add_user", User, ""]),
+ {ok, _} = rabbit_ct_broker_helpers:rabbitmqctl(Config, 0, [
+ "set_permissions", "-p", "/", User, ".*", ".*", ".*"
+ ]),
+ {ok, _} = rabbit_ct_broker_helpers:rabbitmqctl(
+ Config,
+ 0,
+ [
+ "set_topic_permissions",
+ "-p",
+ "/",
+ "guest",
+ "amq.topic",
% Write permission
"test-topic|test-retained-topic|{username}.{client_id}.a|^sp[AB]v\\d+___\\d+",
% Read permission
- "test-topic|test-retained-topic|last-will|{username}.{client_id}.a|^sp[AB]v\\d+___\\d+"]),
+ "test-topic|test-retained-topic|last-will|{username}.{client_id}.a|^sp[AB]v\\d+___\\d+"
+ ]
+ ),
rabbit_ct_helpers:testcase_started(Config, Testcase).
end_per_testcase(Testcase, Config) ->
rabbit_ct_helpers:testcase_finished(Config, Testcase).
-
%% -------------------------------------------------------------------
%% Testsuite cases
%% -------------------------------------------------------------------
@@ -122,5 +140,5 @@ q(P, [K | Rem]) ->
undefined -> undefined;
V -> q(V, Rem)
end;
-q(P, []) -> {ok, P}.
-
+q(P, []) ->
+ {ok, P}.
diff --git a/deps/rabbitmq_mqtt/test/mqtt_machine_SUITE.erl b/deps/rabbitmq_mqtt/test/mqtt_machine_SUITE.erl
index 274877cdc8..ebf773aa44 100644
--- a/deps/rabbitmq_mqtt/test/mqtt_machine_SUITE.erl
+++ b/deps/rabbitmq_mqtt/test/mqtt_machine_SUITE.erl
@@ -12,20 +12,19 @@
all() ->
[
- {group, tests}
+ {group, tests}
].
-
all_tests() ->
[
- basics,
- machine_upgrade,
- many_downs
+ basics,
+ machine_upgrade,
+ many_downs
].
groups() ->
[
- {tests, [], all_tests()}
+ {tests, [], all_tests()}
].
init_per_suite(Config) ->
@@ -53,13 +52,16 @@ end_per_testcase(_TestCase, _Config) ->
basics(_Config) ->
S0 = mqtt_machine:init(#{}),
ClientId = <<"id1">>,
- OthPid = spawn(fun () -> ok end),
+ OthPid = spawn(fun() -> ok end),
{S1, ok, _} = mqtt_machine:apply(meta(1), {register, ClientId, self()}, S0),
?assertMatch(#machine_state{client_ids = Ids} when map_size(Ids) == 1, S1),
?assertMatch(#machine_state{pids = Pids} when map_size(Pids) == 1, S1),
{S2, ok, _} = mqtt_machine:apply(meta(2), {register, ClientId, OthPid}, S1),
- ?assertMatch(#machine_state{client_ids = #{ClientId := OthPid} = Ids}
- when map_size(Ids) == 1, S2),
+ ?assertMatch(
+ #machine_state{client_ids = #{ClientId := OthPid} = Ids} when
+ map_size(Ids) == 1,
+ S2
+ ),
{S3, ok, _} = mqtt_machine:apply(meta(3), {down, OthPid, noproc}, S2),
?assertMatch(#machine_state{client_ids = Ids} when map_size(Ids) == 0, S3),
{S4, ok, _} = mqtt_machine:apply(meta(3), {unregister, ClientId, OthPid}, S2),
@@ -74,41 +76,63 @@ machine_upgrade(_Config) ->
{S1, ok, _} = mqtt_machine_v0:apply(meta(1), {register, ClientId, self()}, S0),
?assertMatch({machine_state, Ids} when map_size(Ids) == 1, S1),
{S2, ok, _} = mqtt_machine:apply(meta(2), {machine_version, 0, 1}, S1),
- ?assertMatch(#machine_state{client_ids = #{ClientId := Self},
- pids = #{Self := [ClientId]} = Pids}
- when map_size(Pids) == 1, S2),
+ ?assertMatch(
+ #machine_state{
+ client_ids = #{ClientId := Self},
+ pids = #{Self := [ClientId]} = Pids
+ } when
+ map_size(Pids) == 1,
+ S2
+ ),
{S3, ok, _} = mqtt_machine:apply(meta(3), {down, self(), noproc}, S2),
- ?assertMatch(#machine_state{client_ids = Ids,
- pids = Pids}
- when map_size(Ids) == 0 andalso map_size(Pids) == 0, S3),
+ ?assertMatch(
+ #machine_state{
+ client_ids = Ids,
+ pids = Pids
+ } when
+ map_size(Ids) == 0 andalso map_size(Pids) == 0,
+ S3
+ ),
ok.
many_downs(_Config) ->
S0 = mqtt_machine:init(#{}),
- Clients = [{list_to_binary(integer_to_list(I)), spawn(fun() -> ok end)}
- || I <- lists:seq(1, 10000)],
+ Clients = [
+ {list_to_binary(integer_to_list(I)), spawn(fun() -> ok end)}
+ || I <- lists:seq(1, 10000)
+ ],
S1 = lists:foldl(
- fun ({ClientId, Pid}, Acc0) ->
- {Acc, ok, _} = mqtt_machine:apply(meta(1), {register, ClientId, Pid}, Acc0),
- Acc
- end, S0, Clients),
+ fun({ClientId, Pid}, Acc0) ->
+ {Acc, ok, _} = mqtt_machine:apply(meta(1), {register, ClientId, Pid}, Acc0),
+ Acc
+ end,
+ S0,
+ Clients
+ ),
_ = lists:foldl(
- fun ({_ClientId, Pid}, Acc0) ->
- {Acc, ok, _} = mqtt_machine:apply(meta(1), {down, Pid, noproc}, Acc0),
- Acc
- end, S1, Clients),
+ fun({_ClientId, Pid}, Acc0) ->
+ {Acc, ok, _} = mqtt_machine:apply(meta(1), {down, Pid, noproc}, Acc0),
+ Acc
+ end,
+ S1,
+ Clients
+ ),
_ = lists:foldl(
- fun ({ClientId, Pid}, Acc0) ->
- {Acc, ok, _} = mqtt_machine:apply(meta(1), {unregister, ClientId,
- Pid}, Acc0),
- Acc
- end, S0, Clients),
+ fun({ClientId, Pid}, Acc0) ->
+ {Acc, ok, _} = mqtt_machine:apply(meta(1), {unregister, ClientId, Pid}, Acc0),
+ Acc
+ end,
+ S0,
+ Clients
+ ),
ok.
%% Utility
meta(Idx) ->
- #{index => Idx,
- term => 1,
- ts => erlang:system_time(millisecond)}.
+ #{
+ index => Idx,
+ term => 1,
+ ts => erlang:system_time(millisecond)
+ }.
diff --git a/deps/rabbitmq_mqtt/test/processor_SUITE.erl b/deps/rabbitmq_mqtt/test/processor_SUITE.erl
index 15ae0dd537..1078462ad8 100644
--- a/deps/rabbitmq_mqtt/test/processor_SUITE.erl
+++ b/deps/rabbitmq_mqtt/test/processor_SUITE.erl
@@ -4,7 +4,6 @@
%%
%% Copyright (c) 2007-2023 VMware, Inc. or its affiliates. All rights reserved.
-
-module(processor_SUITE).
-compile([export_all, nowarn_export_all]).
@@ -14,17 +13,17 @@
all() ->
[
- {group, non_parallel_tests}
+ {group, non_parallel_tests}
].
groups() ->
[
- {non_parallel_tests, [], [
- ignores_colons_in_username_if_option_set,
- interprets_colons_in_username_if_option_not_set,
- get_vhosts_from_global_runtime_parameter,
- get_vhost
- ]}
+ {non_parallel_tests, [], [
+ ignores_colons_in_username_if_option_set,
+ interprets_colons_in_username_if_option_not_set,
+ get_vhosts_from_global_runtime_parameter,
+ get_vhost
+ ]}
].
suite() ->
@@ -42,35 +41,50 @@ init_per_testcase(get_vhost, Config) ->
mnesia:start(),
mnesia:create_table(rabbit_runtime_parameters, [
{attributes, record_info(fields, runtime_parameters)},
- {record_name, runtime_parameters}]),
+ {record_name, runtime_parameters}
+ ]),
Config;
-init_per_testcase(_, Config) -> Config.
+init_per_testcase(_, Config) ->
+ Config.
end_per_testcase(get_vhost, Config) ->
mnesia:stop(),
Config;
-end_per_testcase(_, Config) -> Config.
+end_per_testcase(_, Config) ->
+ Config.
ignore_colons(B) -> application:set_env(rabbitmq_mqtt, ignore_colons_in_username, B).
ignores_colons_in_username_if_option_set(_Config) ->
ignore_colons(true),
- ?assertEqual({rabbit_mqtt_util:env(vhost), <<"a:b:c">>},
- rabbit_mqtt_processor:get_vhost_username(<<"a:b:c">>)).
+ ?assertEqual(
+ {rabbit_mqtt_util:env(vhost), <<"a:b:c">>},
+ rabbit_mqtt_processor:get_vhost_username(<<"a:b:c">>)
+ ).
interprets_colons_in_username_if_option_not_set(_Config) ->
- ignore_colons(false),
- ?assertEqual({<<"a:b">>, <<"c">>},
- rabbit_mqtt_processor:get_vhost_username(<<"a:b:c">>)).
+ ignore_colons(false),
+ ?assertEqual(
+ {<<"a:b">>, <<"c">>},
+ rabbit_mqtt_processor:get_vhost_username(<<"a:b:c">>)
+ ).
get_vhosts_from_global_runtime_parameter(_Config) ->
MappingParameter = [
{<<"O=client,CN=dummy1">>, <<"vhost1">>},
{<<"O=client,CN=dummy2">>, <<"vhost2">>}
],
- <<"vhost1">> = rabbit_mqtt_processor:get_vhost_from_user_mapping(<<"O=client,CN=dummy1">>, MappingParameter),
- <<"vhost2">> = rabbit_mqtt_processor:get_vhost_from_user_mapping(<<"O=client,CN=dummy2">>, MappingParameter),
- undefined = rabbit_mqtt_processor:get_vhost_from_user_mapping(<<"O=client,CN=dummy3">>, MappingParameter),
- undefined = rabbit_mqtt_processor:get_vhost_from_user_mapping(<<"O=client,CN=dummy3">>, not_found).
+ <<"vhost1">> = rabbit_mqtt_processor:get_vhost_from_user_mapping(
+ <<"O=client,CN=dummy1">>, MappingParameter
+ ),
+ <<"vhost2">> = rabbit_mqtt_processor:get_vhost_from_user_mapping(
+ <<"O=client,CN=dummy2">>, MappingParameter
+ ),
+ undefined = rabbit_mqtt_processor:get_vhost_from_user_mapping(
+ <<"O=client,CN=dummy3">>, MappingParameter
+ ),
+ undefined = rabbit_mqtt_processor:get_vhost_from_user_mapping(
+ <<"O=client,CN=dummy3">>, not_found
+ ).
get_vhost(_Config) ->
clear_vhost_global_parameters(),
@@ -83,27 +97,35 @@ get_vhost(_Config) ->
%% not a certificate user, no cert/vhost mapping, vhost in user
%% should use vhost in user
- {_, {<<"somevhost">>, <<"guest">>}} = rabbit_mqtt_processor:get_vhost(<<"somevhost:guest">>, none, 1883),
+ {_, {<<"somevhost">>, <<"guest">>}} = rabbit_mqtt_processor:get_vhost(
+ <<"somevhost:guest">>, none, 1883
+ ),
clear_vhost_global_parameters(),
%% certificate user, no cert/vhost mapping
%% should use default vhost
- {_, {<<"/">>, <<"guest">>}} = rabbit_mqtt_processor:get_vhost(<<"guest">>, <<"O=client,CN=dummy">>, 1883),
+ {_, {<<"/">>, <<"guest">>}} = rabbit_mqtt_processor:get_vhost(
+ <<"guest">>, <<"O=client,CN=dummy">>, 1883
+ ),
clear_vhost_global_parameters(),
%% certificate user, cert/vhost mapping with global runtime parameter
%% should use mapping
set_global_parameter(mqtt_default_vhosts, [
- {<<"O=client,CN=dummy">>, <<"somevhost">>},
+ {<<"O=client,CN=dummy">>, <<"somevhost">>},
{<<"O=client,CN=otheruser">>, <<"othervhost">>}
]),
- {_, {<<"somevhost">>, <<"guest">>}} = rabbit_mqtt_processor:get_vhost(<<"guest">>, <<"O=client,CN=dummy">>, 1883),
+ {_, {<<"somevhost">>, <<"guest">>}} = rabbit_mqtt_processor:get_vhost(
+ <<"guest">>, <<"O=client,CN=dummy">>, 1883
+ ),
clear_vhost_global_parameters(),
%% certificate user, cert/vhost mapping with global runtime parameter, but no key for the user
%% should use default vhost
set_global_parameter(mqtt_default_vhosts, [{<<"O=client,CN=otheruser">>, <<"somevhost">>}]),
- {_, {<<"/">>, <<"guest">>}} = rabbit_mqtt_processor:get_vhost(<<"guest">>, <<"O=client,CN=dummy">>, 1883),
+ {_, {<<"/">>, <<"guest">>}} = rabbit_mqtt_processor:get_vhost(
+ <<"guest">>, <<"O=client,CN=dummy">>, 1883
+ ),
clear_vhost_global_parameters(),
%% not a certificate user, port/vhost mapping
@@ -121,7 +143,9 @@ get_vhost(_Config) ->
{<<"1883">>, <<"somevhost">>},
{<<"1884">>, <<"othervhost">>}
]),
- {_, {<<"vhostinusername">>, <<"guest">>}} = rabbit_mqtt_processor:get_vhost(<<"vhostinusername:guest">>, none, 1883),
+ {_, {<<"vhostinusername">>, <<"guest">>}} = rabbit_mqtt_processor:get_vhost(
+ <<"vhostinusername:guest">>, none, 1883
+ ),
clear_vhost_global_parameters(),
%% not a certificate user, port/vhost mapping, but no mapping for this port
@@ -138,42 +162,50 @@ get_vhost(_Config) ->
{<<"1883">>, <<"somevhost">>},
{<<"1884">>, <<"othervhost">>}
]),
- {_, {<<"somevhost">>, <<"guest">>}} = rabbit_mqtt_processor:get_vhost(<<"guest">>, <<"O=client,CN=dummy">>, 1883),
+ {_, {<<"somevhost">>, <<"guest">>}} = rabbit_mqtt_processor:get_vhost(
+ <<"guest">>, <<"O=client,CN=dummy">>, 1883
+ ),
clear_vhost_global_parameters(),
%% certificate user, port/vhost parameter but no mapping, cert/vhost mapping
%% should use cert/vhost mapping
set_global_parameter(mqtt_default_vhosts, [
- {<<"O=client,CN=dummy">>, <<"somevhost">>},
+ {<<"O=client,CN=dummy">>, <<"somevhost">>},
{<<"O=client,CN=otheruser">>, <<"othervhost">>}
]),
set_global_parameter(mqtt_port_to_vhost_mapping, [
{<<"1884">>, <<"othervhost">>}
]),
- {_, {<<"somevhost">>, <<"guest">>}} = rabbit_mqtt_processor:get_vhost(<<"guest">>, <<"O=client,CN=dummy">>, 1883),
+ {_, {<<"somevhost">>, <<"guest">>}} = rabbit_mqtt_processor:get_vhost(
+ <<"guest">>, <<"O=client,CN=dummy">>, 1883
+ ),
clear_vhost_global_parameters(),
%% certificate user, port/vhost parameter, cert/vhost parameter
%% cert/vhost parameter takes precedence
set_global_parameter(mqtt_default_vhosts, [
- {<<"O=client,CN=dummy">>, <<"cert-somevhost">>},
+ {<<"O=client,CN=dummy">>, <<"cert-somevhost">>},
{<<"O=client,CN=otheruser">>, <<"othervhost">>}
]),
set_global_parameter(mqtt_port_to_vhost_mapping, [
{<<"1883">>, <<"port-vhost">>},
{<<"1884">>, <<"othervhost">>}
]),
- {_, {<<"cert-somevhost">>, <<"guest">>}} = rabbit_mqtt_processor:get_vhost(<<"guest">>, <<"O=client,CN=dummy">>, 1883),
+ {_, {<<"cert-somevhost">>, <<"guest">>}} = rabbit_mqtt_processor:get_vhost(
+ <<"guest">>, <<"O=client,CN=dummy">>, 1883
+ ),
clear_vhost_global_parameters(),
%% certificate user, no port/vhost or cert/vhost mapping, vhost in username
%% should use vhost in username
- {_, {<<"vhostinusername">>, <<"guest">>}} = rabbit_mqtt_processor:get_vhost(<<"vhostinusername:guest">>, <<"O=client,CN=dummy">>, 1883),
+ {_, {<<"vhostinusername">>, <<"guest">>}} = rabbit_mqtt_processor:get_vhost(
+ <<"vhostinusername:guest">>, <<"O=client,CN=dummy">>, 1883
+ ),
%% not a certificate user, port/vhost parameter, cert/vhost parameter
%% port/vhost mapping is used, as cert/vhost should not be used
set_global_parameter(mqtt_default_vhosts, [
- {<<"O=cert">>, <<"cert-somevhost">>},
+ {<<"O=cert">>, <<"cert-somevhost">>},
{<<"O=client,CN=otheruser">>, <<"othervhost">>}
]),
set_global_parameter(mqtt_port_to_vhost_mapping, [
@@ -185,15 +217,15 @@ get_vhost(_Config) ->
ok.
set_global_parameter(Key, Term) ->
- InsertParameterFun = fun () ->
+ InsertParameterFun = fun() ->
mnesia:write(rabbit_runtime_parameters, #runtime_parameters{key = Key, value = Term}, write)
- end,
+ end,
{atomic, ok} = mnesia:transaction(InsertParameterFun).
clear_vhost_global_parameters() ->
- DeleteParameterFun = fun () ->
+ DeleteParameterFun = fun() ->
ok = mnesia:delete(rabbit_runtime_parameters, mqtt_default_vhosts, write),
ok = mnesia:delete(rabbit_runtime_parameters, mqtt_port_to_vhost_mapping, write)
- end,
+ end,
{atomic, ok} = mnesia:transaction(DeleteParameterFun).
diff --git a/deps/rabbitmq_mqtt/test/proxy_protocol_SUITE.erl b/deps/rabbitmq_mqtt/test/proxy_protocol_SUITE.erl
index 551b14c865..6499b9aaea 100644
--- a/deps/rabbitmq_mqtt/test/proxy_protocol_SUITE.erl
+++ b/deps/rabbitmq_mqtt/test/proxy_protocol_SUITE.erl
@@ -34,21 +34,26 @@ init_per_suite(Config) ->
{rabbitmq_ct_tls_verify, verify_none}
]),
MqttConfig = mqtt_config(),
- rabbit_ct_helpers:run_setup_steps(Config1,
- [ fun(Conf) -> merge_app_env(MqttConfig, Conf) end ] ++
+ rabbit_ct_helpers:run_setup_steps(
+ Config1,
+ [fun(Conf) -> merge_app_env(MqttConfig, Conf) end] ++
rabbit_ct_broker_helpers:setup_steps() ++
- rabbit_ct_client_helpers:setup_steps()).
+ rabbit_ct_client_helpers:setup_steps()
+ ).
mqtt_config() ->
{rabbitmq_mqtt, [
- {proxy_protocol, true},
- {ssl_cert_login, true},
- {allow_anonymous, true}]}.
+ {proxy_protocol, true},
+ {ssl_cert_login, true},
+ {allow_anonymous, true}
+ ]}.
end_per_suite(Config) ->
- rabbit_ct_helpers:run_teardown_steps(Config,
+ rabbit_ct_helpers:run_teardown_steps(
+ Config,
rabbit_ct_client_helpers:teardown_steps() ++
- rabbit_ct_broker_helpers:teardown_steps()).
+ rabbit_ct_broker_helpers:teardown_steps()
+ ).
init_per_group(_, Config) -> Config.
end_per_group(_, Config) -> Config.
@@ -61,8 +66,11 @@ end_per_testcase(Testcase, Config) ->
proxy_protocol(Config) ->
Port = rabbit_ct_broker_helpers:get_node_config(Config, 0, tcp_port_mqtt),
- {ok, Socket} = gen_tcp:connect({127,0,0,1}, Port,
- [binary, {active, false}, {packet, raw}]),
+ {ok, Socket} = gen_tcp:connect(
+ {127, 0, 0, 1},
+ Port,
+ [binary, {active, false}, {packet, raw}]
+ ),
ok = inet:send(Socket, "PROXY TCP4 192.168.1.1 192.168.1.2 80 81\r\n"),
ok = inet:send(Socket, mqtt_3_1_1_connect_packet()),
{ok, _Packet} = gen_tcp:recv(Socket, 0, ?TIMEOUT),
@@ -75,8 +83,11 @@ proxy_protocol(Config) ->
proxy_protocol_tls(Config) ->
app_utils:start_applications([asn1, crypto, public_key, ssl]),
Port = rabbit_ct_broker_helpers:get_node_config(Config, 0, tcp_port_mqtt_tls),
- {ok, Socket} = gen_tcp:connect({127,0,0,1}, Port,
- [binary, {active, false}, {packet, raw}]),
+ {ok, Socket} = gen_tcp:connect(
+ {127, 0, 0, 1},
+ Port,
+ [binary, {active, false}, {packet, raw}]
+ ),
ok = inet:send(Socket, "PROXY TCP4 192.168.1.1 192.168.1.2 80 81\r\n"),
{ok, SslSocket} = ssl:connect(Socket, [], ?TIMEOUT),
ok = ssl:send(SslSocket, mqtt_3_1_1_connect_packet()),
@@ -96,29 +107,5 @@ merge_app_env(MqttConfig, Config) ->
rabbit_ct_helpers:merge_app_env(Config, MqttConfig).
mqtt_3_1_1_connect_packet() ->
- <<16,
- 24,
- 0,
- 4,
- 77,
- 81,
- 84,
- 84,
- 4,
- 2,
- 0,
- 60,
- 0,
- 12,
- 84,
- 101,
- 115,
- 116,
- 67,
- 111,
- 110,
- 115,
- 117,
- 109,
- 101,
- 114>>.
+ <<16, 24, 0, 4, 77, 81, 84, 84, 4, 2, 0, 60, 0, 12, 84, 101, 115, 116, 67, 111, 110, 115, 117,
+ 109, 101, 114>>.
diff --git a/deps/rabbitmq_mqtt/test/rabbit_auth_backend_mqtt_mock.erl b/deps/rabbitmq_mqtt/test/rabbit_auth_backend_mqtt_mock.erl
index 69fea6c221..ad1bfe1e58 100644
--- a/deps/rabbitmq_mqtt/test/rabbit_auth_backend_mqtt_mock.erl
+++ b/deps/rabbitmq_mqtt/test/rabbit_auth_backend_mqtt_mock.erl
@@ -13,11 +13,16 @@
-behaviour(rabbit_authn_backend).
-behaviour(rabbit_authz_backend).
--export([setup/1,
- user_login_authentication/2, user_login_authorization/2,
- check_vhost_access/3, check_resource_access/4, check_topic_access/4,
- state_can_expire/0,
- get/1]).
+-export([
+ setup/1,
+ user_login_authentication/2,
+ user_login_authorization/2,
+ check_vhost_access/3,
+ check_resource_access/4,
+ check_topic_access/4,
+ state_can_expire/0,
+ get/1
+]).
setup(CallerPid) ->
ets:new(?MODULE, [set, public, named_table]),
@@ -26,12 +31,13 @@ setup(CallerPid) ->
stop -> ok
end.
-
user_login_authentication(_, AuthProps) ->
ets:insert(?MODULE, {authentication, AuthProps}),
- {ok, #auth_user{username = <<"dummy">>,
- tags = [],
- impl = none}}.
+ {ok, #auth_user{
+ username = <<"dummy">>,
+ tags = [],
+ impl = none
+ }}.
user_login_authorization(_, _) ->
io:format("login authorization"),
diff --git a/deps/rabbitmq_mqtt/test/reader_SUITE.erl b/deps/rabbitmq_mqtt/test/reader_SUITE.erl
index 4c65e20e47..5a96612231 100644
--- a/deps/rabbitmq_mqtt/test/reader_SUITE.erl
+++ b/deps/rabbitmq_mqtt/test/reader_SUITE.erl
@@ -5,42 +5,45 @@
%% Copyright (c) 2007-2023 VMware, Inc. or its affiliates. All rights reserved.
%%
-module(reader_SUITE).
--compile([export_all,
- nowarn_export_all]).
+-compile([
+ export_all,
+ nowarn_export_all
+]).
-include_lib("common_test/include/ct.hrl").
-include_lib("eunit/include/eunit.hrl").
-import(rabbit_ct_broker_helpers, [rpc/4]).
-import(rabbit_ct_helpers, [eventually/3]).
--import(util, [all_connection_pids/1,
- publish_qos1_timeout/4,
- expect_publishes/3,
- connect/2,
- connect/3,
- await_exit/1]).
+-import(util, [
+ all_connection_pids/1,
+ publish_qos1_timeout/4,
+ expect_publishes/3,
+ connect/2,
+ connect/3,
+ await_exit/1
+]).
all() ->
[
- {group, tests}
+ {group, tests}
].
groups() ->
[
- {tests, [],
- [
- block_connack_timeout,
- handle_invalid_packets,
- login_timeout,
- stats,
- quorum_clean_session_false,
- quorum_clean_session_true,
- classic_clean_session_true,
- classic_clean_session_false,
- non_clean_sess_empty_client_id,
- event_authentication_failure,
- rabbit_mqtt_qos0_queue_overflow
- ]}
+ {tests, [], [
+ block_connack_timeout,
+ handle_invalid_packets,
+ login_timeout,
+ stats,
+ quorum_clean_session_false,
+ quorum_clean_session_true,
+ classic_clean_session_true,
+ classic_clean_session_false,
+ non_clean_sess_empty_client_id,
+ event_authentication_failure,
+ rabbit_mqtt_qos0_queue_overflow
+ ]}
].
suite() ->
@@ -51,28 +54,36 @@ suite() ->
%% -------------------------------------------------------------------
merge_app_env(Config) ->
- rabbit_ct_helpers:merge_app_env(Config,
- {rabbit, [
- {collect_statistics, basic},
- {collect_statistics_interval, 100}
- ]}).
+ rabbit_ct_helpers:merge_app_env(
+ Config,
+ {rabbit, [
+ {collect_statistics, basic},
+ {collect_statistics_interval, 100}
+ ]}
+ ).
init_per_suite(Config) ->
rabbit_ct_helpers:log_environment(),
Config1 = rabbit_ct_helpers:set_config(Config, [
{rmq_nodename_suffix, ?MODULE},
- {rmq_extra_tcp_ports, [tcp_port_mqtt_extra,
- tcp_port_mqtt_tls_extra]}
- ]),
- rabbit_ct_helpers:run_setup_steps(Config1,
- [ fun merge_app_env/1 ] ++
- rabbit_ct_broker_helpers:setup_steps() ++
- rabbit_ct_client_helpers:setup_steps()).
+ {rmq_extra_tcp_ports, [
+ tcp_port_mqtt_extra,
+ tcp_port_mqtt_tls_extra
+ ]}
+ ]),
+ rabbit_ct_helpers:run_setup_steps(
+ Config1,
+ [fun merge_app_env/1] ++
+ rabbit_ct_broker_helpers:setup_steps() ++
+ rabbit_ct_client_helpers:setup_steps()
+ ).
end_per_suite(Config) ->
- rabbit_ct_helpers:run_teardown_steps(Config,
- rabbit_ct_client_helpers:teardown_steps() ++
- rabbit_ct_broker_helpers:teardown_steps()).
+ rabbit_ct_helpers:run_teardown_steps(
+ Config,
+ rabbit_ct_client_helpers:teardown_steps() ++
+ rabbit_ct_broker_helpers:teardown_steps()
+ ).
init_per_group(_, Config) ->
Config.
@@ -86,7 +97,6 @@ init_per_testcase(Testcase, Config) ->
end_per_testcase(Testcase, Config) ->
rabbit_ct_helpers:testcase_finished(Config, Testcase).
-
%% -------------------------------------------------------------------
%% Testsuite cases
%% -------------------------------------------------------------------
@@ -100,11 +110,13 @@ block_connack_timeout(Config) ->
timer:sleep(100),
%% We can still connect via TCP, but CONNECT packet will not be processed on the server.
- {ok, Client} = emqtt:start_link([{host, "localhost"},
- {port, P},
- {clientid, atom_to_binary(?FUNCTION_NAME)},
- {proto_ver, v4},
- {connect_timeout, 1}]),
+ {ok, Client} = emqtt:start_link([
+ {host, "localhost"},
+ {port, P},
+ {clientid, atom_to_binary(?FUNCTION_NAME)},
+ {proto_ver, v4},
+ {connect_timeout, 1}
+ ]),
unlink(Client),
ClientMRef = monitor(process, Client),
{error, connack_timeout} = emqtt:connect(Client),
@@ -112,7 +124,7 @@ block_connack_timeout(Config) ->
{'DOWN', ClientMRef, process, Client, connack_timeout} ->
ok
after 200 ->
- ct:fail("missing connack_timeout in client")
+ ct:fail("missing connack_timeout in client")
end,
Ports = rpc(Config, erlang, ports, []),
@@ -130,7 +142,7 @@ block_connack_timeout(Config) ->
%% because our client already disconnected.
ok
after 2000 ->
- ct:fail("missing peername_not_known from server")
+ ct:fail("missing peername_not_known from server")
end,
%% Ensure that our client is not registered.
?assertEqual([], all_connection_pids(Config)),
@@ -170,8 +182,12 @@ stats(Config) ->
[{Pid, Props}] = rpc(Config, ets, lookup, [connection_metrics, Pid]),
true = proplists:is_defined(garbage_collection, Props),
%% If the coarse entry is present, stats were successfully emitted
- [{Pid, _, _, _, _}] = rpc(Config, ets, lookup,
- [connection_coarse_metrics, Pid]),
+ [{Pid, _, _, _, _}] = rpc(
+ Config,
+ ets,
+ lookup,
+ [connection_coarse_metrics, Pid]
+ ),
ok = emqtt:disconnect(C).
get_durable_queue_type(Server, QNameBin) ->
@@ -216,32 +232,41 @@ classic_clean_session_true(Config) ->
validate_durable_queue_type(Config, <<"classicCleanSessionTrue">>, true, rabbit_classic_queue).
classic_clean_session_false(Config) ->
- validate_durable_queue_type(Config, <<"classicCleanSessionFalse">>, false, rabbit_classic_queue).
+ validate_durable_queue_type(
+ Config, <<"classicCleanSessionFalse">>, false, rabbit_classic_queue
+ ).
%% "If the Client supplies a zero-byte ClientId with CleanSession set to 0,
%% the Server MUST respond to the CONNECT Packet with a CONNACK return code 0x02
%% (Identifier rejected) and then close the Network Connection" [MQTT-3.1.3-8].
non_clean_sess_empty_client_id(Config) ->
{ok, C} = emqtt:start_link(
- [{clientid, <<>>},
- {clean_start, false},
- {proto_ver, v4},
- {host, "localhost"},
- {port, rabbit_ct_broker_helpers:get_node_config(Config, 0, tcp_port_mqtt)}
- ]),
+ [
+ {clientid, <<>>},
+ {clean_start, false},
+ {proto_ver, v4},
+ {host, "localhost"},
+ {port, rabbit_ct_broker_helpers:get_node_config(Config, 0, tcp_port_mqtt)}
+ ]
+ ),
process_flag(trap_exit, true),
- ?assertMatch({error, {client_identifier_not_valid, _}},
- emqtt:connect(C)),
+ ?assertMatch(
+ {error, {client_identifier_not_valid, _}},
+ emqtt:connect(C)
+ ),
ok = await_exit(C).
event_authentication_failure(Config) ->
{ok, C} = emqtt:start_link(
- [{username, <<"Trudy">>},
- {password, <<"fake-password">>},
- {host, "localhost"},
- {port, rabbit_ct_broker_helpers:get_node_config(Config, 0, tcp_port_mqtt)},
- {clientid, atom_to_binary(?FUNCTION_NAME)},
- {proto_ver, v4}]),
+ [
+ {username, <<"Trudy">>},
+ {password, <<"fake-password">>},
+ {host, "localhost"},
+ {port, rabbit_ct_broker_helpers:get_node_config(Config, 0, tcp_port_mqtt)},
+ {clientid, atom_to_binary(?FUNCTION_NAME)},
+ {proto_ver, v4}
+ ]
+ ),
true = unlink(C),
ok = rabbit_ct_broker_helpers:add_code_path_to_all_nodes(Config, event_recorder),
@@ -252,9 +277,13 @@ event_authentication_failure(Config) ->
[E, _ConnectionClosedEvent] = util:get_events(Server),
util:assert_event_type(user_authentication_failure, E),
- util:assert_event_prop([{name, <<"Trudy">>},
- {connection_type, network}],
- E),
+ util:assert_event_prop(
+ [
+ {name, <<"Trudy">>},
+ {connection_type, network}
+ ],
+ E
+ ),
ok = gen_event:delete_handler({rabbit_event, Server}, event_recorder, []).
@@ -266,8 +295,12 @@ rabbit_mqtt_qos0_queue_overflow(Config) ->
NumMsgs = 10_000,
%% Provoke TCP back-pressure from client to server by using very small buffers.
- Opts = [{tcp_opts, [{recbuf, 512},
- {buffer, 512}]}],
+ Opts = [
+ {tcp_opts, [
+ {recbuf, 512},
+ {buffer, 512}
+ ]}
+ ],
Sub = connect(<<"subscriber">>, Config, Opts),
{ok, _, [0]} = emqtt:subscribe(Sub, Topic, qos0),
[ServerConnectionPid] = all_connection_pids(Config),
@@ -279,9 +312,12 @@ rabbit_mqtt_qos0_queue_overflow(Config) ->
%% Let's overflow the receiving server MQTT connection process
%% (i.e. the rabbit_mqtt_qos0_queue) by sending many large messages.
Pub = connect(<<"publisher">>, Config),
- lists:foreach(fun(_) ->
- ok = emqtt:publish(Pub, Topic, Msg, qos0)
- end, lists:seq(1, NumMsgs)),
+ lists:foreach(
+ fun(_) ->
+ ok = emqtt:publish(Pub, Topic, Msg, qos0)
+ end,
+ lists:seq(1, NumMsgs)
+ ),
%% Give the server some time to process (either send or drop) the messages.
timer:sleep(2000),
@@ -318,9 +354,11 @@ rabbit_mqtt_qos0_queue_overflow(Config) ->
num_received(Topic, Payload, N) ->
receive
- {publish, #{topic := Topic,
- payload := Payload}} ->
+ {publish, #{
+ topic := Topic,
+ payload := Payload
+ }} ->
num_received(Topic, Payload, N + 1)
after 1000 ->
- N
+ N
end.
diff --git a/deps/rabbitmq_mqtt/test/retainer_SUITE.erl b/deps/rabbitmq_mqtt/test/retainer_SUITE.erl
index 3a2585e8fe..eb3f8dedd5 100644
--- a/deps/rabbitmq_mqtt/test/retainer_SUITE.erl
+++ b/deps/rabbitmq_mqtt/test/retainer_SUITE.erl
@@ -8,29 +8,31 @@
-compile([export_all, nowarn_export_all]).
-include_lib("common_test/include/ct.hrl").
--import(util, [expect_publishes/3,
- connect/3]).
+-import(util, [
+ expect_publishes/3,
+ connect/3
+]).
all() ->
[
- {group, dets},
- {group, ets},
- {group, noop}
+ {group, dets},
+ {group, ets},
+ {group, noop}
].
groups() ->
[
- {dets, [], tests()},
- {ets, [], tests()},
- {noop, [], [does_not_retain]}
+ {dets, [], tests()},
+ {ets, [], tests()},
+ {noop, [], [does_not_retain]}
].
tests() ->
[
- coerce_configuration_data,
- should_translate_amqp2mqtt_on_publish,
- should_translate_amqp2mqtt_on_retention,
- should_translate_amqp2mqtt_on_retention_search
+ coerce_configuration_data,
+ should_translate_amqp2mqtt_on_publish,
+ should_translate_amqp2mqtt_on_retention,
+ should_translate_amqp2mqtt_on_retention_search
].
suite() ->
@@ -49,31 +51,38 @@ end_per_suite(Config) ->
init_per_group(Group, Config0) ->
Config = rabbit_ct_helpers:set_config(
- Config0,
- [
- {rmq_nodename_suffix, Group},
- {rmq_extra_tcp_ports, [tcp_port_mqtt_extra,
- tcp_port_mqtt_tls_extra]}
- ]),
+ Config0,
+ [
+ {rmq_nodename_suffix, Group},
+ {rmq_extra_tcp_ports, [
+ tcp_port_mqtt_extra,
+ tcp_port_mqtt_tls_extra
+ ]}
+ ]
+ ),
Mod = list_to_atom("rabbit_mqtt_retained_msg_store_" ++ atom_to_list(Group)),
- Env = [{rabbitmq_mqtt, [{retained_message_store, Mod}]},
- {rabbit, [
- {default_user, "guest"},
- {default_pass, "guest"},
- {default_vhost, "/"},
- {default_permissions, [".*", ".*", ".*"]}
- ]}],
+ Env = [
+ {rabbitmq_mqtt, [{retained_message_store, Mod}]},
+ {rabbit, [
+ {default_user, "guest"},
+ {default_pass, "guest"},
+ {default_vhost, "/"},
+ {default_permissions, [".*", ".*", ".*"]}
+ ]}
+ ],
rabbit_ct_helpers:run_setup_steps(
- Config,
- [fun(Conf) -> rabbit_ct_helpers:merge_app_env(Conf, Env) end] ++
- rabbit_ct_broker_helpers:setup_steps() ++
- rabbit_ct_client_helpers:setup_steps()).
+ Config,
+ [fun(Conf) -> rabbit_ct_helpers:merge_app_env(Conf, Env) end] ++
+ rabbit_ct_broker_helpers:setup_steps() ++
+ rabbit_ct_client_helpers:setup_steps()
+ ).
end_per_group(_, Config) ->
rabbit_ct_helpers:run_teardown_steps(
- Config,
- rabbit_ct_client_helpers:teardown_steps() ++
- rabbit_ct_broker_helpers:teardown_steps()).
+ Config,
+ rabbit_ct_client_helpers:teardown_steps() ++
+ rabbit_ct_broker_helpers:teardown_steps()
+ ).
init_per_testcase(Testcase, Config) ->
rabbit_ct_helpers:testcase_started(Config, Testcase).
@@ -81,7 +90,6 @@ init_per_testcase(Testcase, Config) ->
end_per_testcase(Testcase, Config) ->
rabbit_ct_helpers:testcase_finished(Config, Testcase).
-
%% -------------------------------------------------------------------
%% Testsuite cases
%% -------------------------------------------------------------------
@@ -104,7 +112,7 @@ should_translate_amqp2mqtt_on_publish(Config) ->
C = connect(<<"simpleClientRetainer">>, Config, [{ack_timeout, 1}]),
%% there's an active consumer
{ok, _, _} = emqtt:subscribe(C, <<"TopicA/Device.Field">>, qos1),
- ok = emqtt:publish(C, <<"TopicA/Device.Field">>, #{}, <<"Payload">>, [{retain, true}]),
+ ok = emqtt:publish(C, <<"TopicA/Device.Field">>, #{}, <<"Payload">>, [{retain, true}]),
ok = expect_publishes(C, <<"TopicA/Device/Field">>, [<<"Payload">>]),
ok = emqtt:disconnect(C).
@@ -116,7 +124,7 @@ should_translate_amqp2mqtt_on_publish(Config) ->
should_translate_amqp2mqtt_on_retention(Config) ->
C = connect(<<"simpleClientRetainer">>, Config, [{ack_timeout, 1}]),
%% publish with retain = true before a consumer comes around
- ok = emqtt:publish(C, <<"TopicA/Device.Field">>, #{}, <<"Payload">>, [{retain, true}]),
+ ok = emqtt:publish(C, <<"TopicA/Device.Field">>, #{}, <<"Payload">>, [{retain, true}]),
{ok, _, _} = emqtt:subscribe(C, <<"TopicA/Device.Field">>, qos1),
ok = expect_publishes(C, <<"TopicA/Device/Field">>, [<<"Payload">>]),
ok = emqtt:disconnect(C).
@@ -128,19 +136,19 @@ should_translate_amqp2mqtt_on_retention(Config) ->
%% -------------------------------------------------------------------
should_translate_amqp2mqtt_on_retention_search(Config) ->
C = connect(<<"simpleClientRetainer">>, Config, [{ack_timeout, 1}]),
- ok = emqtt:publish(C, <<"TopicA/Device.Field">>, #{}, <<"Payload">>, [{retain, true}]),
+ ok = emqtt:publish(C, <<"TopicA/Device.Field">>, #{}, <<"Payload">>, [{retain, true}]),
{ok, _, _} = emqtt:subscribe(C, <<"TopicA/Device/Field">>, qos1),
ok = expect_publishes(C, <<"TopicA/Device/Field">>, [<<"Payload">>]),
ok = emqtt:disconnect(C).
does_not_retain(Config) ->
C = connect(<<"simpleClientRetainer">>, Config, [{ack_timeout, 1}]),
- ok = emqtt:publish(C, <<"TopicA/Device.Field">>, #{}, <<"Payload">>, [{retain, true}]),
+ ok = emqtt:publish(C, <<"TopicA/Device.Field">>, #{}, <<"Payload">>, [{retain, true}]),
{ok, _, _} = emqtt:subscribe(C, <<"TopicA/Device.Field">>, qos1),
receive
Unexpected ->
ct:fail("Unexpected message: ~p", [Unexpected])
after 1000 ->
- ok
+ ok
end,
ok = emqtt:disconnect(C).
diff --git a/deps/rabbitmq_mqtt/test/shared_SUITE.erl b/deps/rabbitmq_mqtt/test/shared_SUITE.erl
index 71183d5f9c..32d46f0481 100644
--- a/deps/rabbitmq_mqtt/test/shared_SUITE.erl
+++ b/deps/rabbitmq_mqtt/test/shared_SUITE.erl
@@ -6,8 +6,10 @@
%% Test suite shared between rabbitmq_mqtt and rabbitmq_web_mqtt.
-module(shared_SUITE).
--compile([export_all,
- nowarn_export_all]).
+-compile([
+ export_all,
+ nowarn_export_all
+]).
-include_lib("common_test/include/ct.hrl").
-include_lib("eunit/include/eunit.hrl").
@@ -15,100 +17,113 @@
-include_lib("rabbitmq_ct_helpers/include/rabbit_assert.hrl").
-include_lib("rabbitmq_ct_helpers/include/rabbit_mgmt_test.hrl").
--import(rabbit_ct_broker_helpers,
- [rabbitmqctl_list/3,
- rpc/4,
- rpc/5,
- rpc_all/4,
- get_node_config/3,
- drain_node/2,
- revive_node/2
- ]).
--import(rabbit_ct_helpers,
- [eventually/3,
- eventually/1]).
--import(util,
- [all_connection_pids/1,
- get_global_counters/2, get_global_counters/3, get_global_counters/4,
- expect_publishes/3,
- connect/2, connect/3, connect/4,
- get_events/1, assert_event_type/2, assert_event_prop/2,
- await_exit/1, await_exit/2,
- publish_qos1_timeout/4]).
--import(rabbit_mgmt_test_util,
- [http_get/2,
- http_delete/3]).
+-import(
+ rabbit_ct_broker_helpers,
+ [
+ rabbitmqctl_list/3,
+ rpc/4,
+ rpc/5,
+ rpc_all/4,
+ get_node_config/3,
+ drain_node/2,
+ revive_node/2
+ ]
+).
+-import(
+ rabbit_ct_helpers,
+ [
+ eventually/3,
+ eventually/1
+ ]
+).
+-import(
+ util,
+ [
+ all_connection_pids/1,
+ get_global_counters/2, get_global_counters/3, get_global_counters/4,
+ expect_publishes/3,
+ connect/2, connect/3, connect/4,
+ get_events/1,
+ assert_event_type/2,
+ assert_event_prop/2,
+ await_exit/1, await_exit/2,
+ publish_qos1_timeout/4
+ ]
+).
+-import(
+ rabbit_mgmt_test_util,
+ [
+ http_get/2,
+ http_delete/3
+ ]
+).
all() ->
[
- {group, mqtt}
- ,{group, web_mqtt}
+ {group, mqtt},
+ {group, web_mqtt}
].
groups() ->
[
- {mqtt, [], subgroups()}
- ,{web_mqtt, [], subgroups()}
+ {mqtt, [], subgroups()},
+ {web_mqtt, [], subgroups()}
].
subgroups() ->
[
- {cluster_size_1, [],
- [
- {global_counters, [],
- [
- global_counters_v3,
- global_counters_v4
+ {cluster_size_1, [], [
+ {global_counters, [], [
+ global_counters_v3,
+ global_counters_v4
+ ]},
+ {tests, [], [
+ block_only_publisher,
+ many_qos1_messages,
+ subscription_ttl,
+ management_plugin_connection,
+ management_plugin_enable,
+ disconnect,
+ pubsub_shared_connection,
+ pubsub_separate_connections,
+ will_with_disconnect,
+ will_without_disconnect,
+ quorum_queue_rejects,
+ events,
+ internal_event_handler,
+ non_clean_sess_reconnect_qos1,
+ non_clean_sess_reconnect_qos0,
+ non_clean_sess_reconnect_qos0_and_qos1,
+ subscribe_same_topic_same_qos,
+ subscribe_same_topic_different_qos,
+ subscribe_multiple,
+ large_message_mqtt_to_mqtt,
+ large_message_amqp_to_mqtt,
+ keepalive,
+ keepalive_turned_off,
+ duplicate_client_id,
+ block,
+ amqp_to_mqtt_qos0,
+ clean_session_disconnect_client,
+ clean_session_kill_node,
+ rabbit_status_connection_count,
+ trace
+ ]}
]},
- {tests, [],
- [
- block_only_publisher
- ,many_qos1_messages
- ,subscription_ttl
- ,management_plugin_connection
- ,management_plugin_enable
- ,disconnect
- ,pubsub_shared_connection
- ,pubsub_separate_connections
- ,will_with_disconnect
- ,will_without_disconnect
- ,quorum_queue_rejects
- ,events
- ,internal_event_handler
- ,non_clean_sess_reconnect_qos1
- ,non_clean_sess_reconnect_qos0
- ,non_clean_sess_reconnect_qos0_and_qos1
- ,subscribe_same_topic_same_qos
- ,subscribe_same_topic_different_qos
- ,subscribe_multiple
- ,large_message_mqtt_to_mqtt
- ,large_message_amqp_to_mqtt
- ,keepalive
- ,keepalive_turned_off
- ,duplicate_client_id
- ,block
- ,amqp_to_mqtt_qos0
- ,clean_session_disconnect_client
- ,clean_session_kill_node
- ,rabbit_status_connection_count
- ,trace
+ {cluster_size_3, [], [
+ queue_down_qos1,
+ consuming_classic_mirrored_queue_down,
+ consuming_classic_queue_down,
+ flow_classic_mirrored_queue,
+ flow_quorum_queue,
+ flow_stream,
+ rabbit_mqtt_qos0_queue,
+ cli_list_queues,
+ maintenance,
+ delete_create_queue,
+ publish_to_all_queue_types_qos0,
+ publish_to_all_queue_types_qos1
]}
- ]},
- {cluster_size_3, [],
- [
- queue_down_qos1,
- consuming_classic_mirrored_queue_down,
- consuming_classic_queue_down,
- flow_classic_mirrored_queue,
- flow_quorum_queue,
- flow_stream,
- rabbit_mqtt_qos0_queue,
- cli_list_queues,
- maintenance,
- delete_create_queue,
- publish_to_all_queue_types_qos0,
- publish_to_all_queue_types_qos1
- ]}
].
suite() ->
@@ -129,54 +144,67 @@ init_per_group(mqtt, Config) ->
rabbit_ct_helpers:set_config(Config, {websocket, false});
init_per_group(web_mqtt, Config) ->
rabbit_ct_helpers:set_config(Config, {websocket, true});
-
init_per_group(cluster_size_1, Config) ->
rabbit_ct_helpers:set_config(Config, [{rmq_nodes_count, 1}]);
init_per_group(cluster_size_3 = Group, Config) ->
- init_per_group0(Group,
- rabbit_ct_helpers:set_config(Config, [{rmq_nodes_count, 3}]));
-init_per_group(Group, Config)
- when Group =:= global_counters orelse
- Group =:= tests ->
+ init_per_group0(
+ Group,
+ rabbit_ct_helpers:set_config(Config, [{rmq_nodes_count, 3}])
+ );
+init_per_group(Group, Config) when
+ Group =:= global_counters orelse
+ Group =:= tests
+->
init_per_group0(Group, Config).
init_per_group0(Group, Config0) ->
Suffix = lists:flatten(io_lib:format("~s_websocket_~w", [Group, ?config(websocket, Config0)])),
Config1 = rabbit_ct_helpers:set_config(
- Config0,
- [{rmq_nodename_suffix, Suffix},
- {rmq_extra_tcp_ports, [tcp_port_mqtt_extra,
- tcp_port_mqtt_tls_extra]}]),
+ Config0,
+ [
+ {rmq_nodename_suffix, Suffix},
+ {rmq_extra_tcp_ports, [
+ tcp_port_mqtt_extra,
+ tcp_port_mqtt_tls_extra
+ ]}
+ ]
+ ),
Config = rabbit_ct_helpers:merge_app_env(
- Config1,
- {rabbit, [{classic_queue_default_version, 2}]}),
+ Config1,
+ {rabbit, [{classic_queue_default_version, 2}]}
+ ),
rabbit_ct_helpers:run_steps(
- Config,
- rabbit_ct_broker_helpers:setup_steps() ++
- rabbit_ct_client_helpers:setup_steps()).
-
-end_per_group(G, Config)
- when G =:= mqtt;
- G =:= web_mqtt;
- G =:= cluster_size_1 ->
+ Config,
+ rabbit_ct_broker_helpers:setup_steps() ++
+ rabbit_ct_client_helpers:setup_steps()
+ ).
+
+end_per_group(G, Config) when
+ G =:= mqtt;
+ G =:= web_mqtt;
+ G =:= cluster_size_1
+->
Config;
end_per_group(_, Config) ->
rabbit_ct_helpers:run_teardown_steps(
- Config,
- rabbit_ct_client_helpers:teardown_steps() ++
- rabbit_ct_broker_helpers:teardown_steps()).
+ Config,
+ rabbit_ct_client_helpers:teardown_steps() ++
+ rabbit_ct_broker_helpers:teardown_steps()
+ ).
init_per_testcase(Testcase = maintenance, Config) ->
case rabbit_ct_helpers:is_mixed_versions() of
true ->
- {skip, "maintenance mode wrongly closes cluster-wide MQTT connections "
- "in RMQ < 3.11.2 and < 3.10.10"};
+ {skip,
+ "maintenance mode wrongly closes cluster-wide MQTT connections "
+ "in RMQ < 3.11.2 and < 3.10.10"};
false ->
init_per_testcase0(Testcase, Config)
end;
-init_per_testcase(T, Config)
- when T =:= management_plugin_connection;
- T =:= management_plugin_enable ->
+init_per_testcase(T, Config) when
+ T =:= management_plugin_connection;
+ T =:= management_plugin_enable
+->
ok = inets:start(),
init_per_testcase0(T, Config);
init_per_testcase(Testcase, Config) ->
@@ -187,9 +215,10 @@ init_per_testcase0(Testcase, Config) ->
[ok = rabbit_ct_broker_helpers:enable_plugin(Config, N, rabbitmq_web_mqtt) || N <- Nodes],
rabbit_ct_helpers:testcase_started(Config, Testcase).
-end_per_testcase(T, Config)
- when T =:= management_plugin_connection;
- T =:= management_plugin_enable ->
+end_per_testcase(T, Config) when
+ T =:= management_plugin_connection;
+ T =:= management_plugin_enable
+->
ok = inets:stop(),
end_per_testcase0(T, Config);
end_per_testcase(Testcase, Config) ->
@@ -219,11 +248,14 @@ pubsub_shared_connection(Config) ->
{ok, _, [1]} = emqtt:subscribe(C, Topic, qos1),
Payload = <<"a\x00a">>,
- ?assertMatch({ok, #{packet_id := _,
- reason_code := 0,
- reason_code_name := success
- }},
- emqtt:publish(C, Topic, Payload, [{qos, 1}])),
+ ?assertMatch(
+ {ok, #{
+ packet_id := _,
+ reason_code := 0,
+ reason_code_name := success
+ }},
+ emqtt:publish(C, Topic, Payload, [{qos, 1}])
+ ),
ok = expect_publishes(C, Topic, [Payload]),
ok = emqtt:disconnect(C).
@@ -235,11 +267,14 @@ pubsub_separate_connections(Config) ->
{ok, _, [1]} = emqtt:subscribe(Sub, Topic, qos1),
Payload = <<"a\x00a">>,
- ?assertMatch({ok, #{packet_id := _,
- reason_code := 0,
- reason_code_name := success
- }},
- emqtt:publish(Pub, Topic, Payload, [{qos, 1}])),
+ ?assertMatch(
+ {ok, #{
+ packet_id := _,
+ reason_code := 0,
+ reason_code_name := success
+ }},
+ emqtt:publish(Pub, Topic, Payload, [{qos, 1}])
+ ),
ok = expect_publishes(Sub, Topic, [Payload]),
ok = emqtt:disconnect(Pub),
ok = emqtt:disconnect(Sub).
@@ -247,26 +282,32 @@ pubsub_separate_connections(Config) ->
will_with_disconnect(Config) ->
LastWillTopic = <<"/topic/last-will">>,
LastWillMsg = <<"last will message">>,
- PubOpts = [{will_topic, LastWillTopic},
- {will_payload, LastWillMsg},
- {will_qos, 1}],
+ PubOpts = [
+ {will_topic, LastWillTopic},
+ {will_payload, LastWillMsg},
+ {will_qos, 1}
+ ],
Pub = connect(<<(atom_to_binary(?FUNCTION_NAME))/binary, "_publisher">>, Config, PubOpts),
Sub = connect(<<(atom_to_binary(?FUNCTION_NAME))/binary, "_subscriber">>, Config),
{ok, _, [1]} = emqtt:subscribe(Sub, LastWillTopic, qos1),
%% Client sends DISCONNECT packet. Therefore, will message should not be sent.
ok = emqtt:disconnect(Pub),
- ?assertEqual({publish_not_received, LastWillMsg},
- expect_publishes(Sub, LastWillTopic, [LastWillMsg])),
+ ?assertEqual(
+ {publish_not_received, LastWillMsg},
+ expect_publishes(Sub, LastWillTopic, [LastWillMsg])
+ ),
ok = emqtt:disconnect(Sub).
will_without_disconnect(Config) ->
LastWillTopic = <<"/topic/last-will">>,
LastWillMsg = <<"last will message">>,
- PubOpts = [{will_topic, LastWillTopic},
- {will_payload, LastWillMsg},
- {will_qos, 1}],
+ PubOpts = [
+ {will_topic, LastWillTopic},
+ {will_payload, LastWillMsg},
+ {will_qos, 1}
+ ],
Pub = connect(<<(atom_to_binary(?FUNCTION_NAME))/binary, "_publisher">>, Config, PubOpts),
timer:sleep(100),
[ServerPublisherPid] = all_connection_pids(Config),
@@ -285,8 +326,11 @@ quorum_queue_rejects(Config) ->
Name = atom_to_binary(?FUNCTION_NAME),
ok = rabbit_ct_broker_helpers:set_policy(
- Config, 0, <<"qq-policy">>, Name, <<"queues">>, [{<<"max-length">>, 1},
- {<<"overflow">>, <<"reject-publish">>}]),
+ Config, 0, <<"qq-policy">>, Name, <<"queues">>, [
+ {<<"max-length">>, 1},
+ {<<"overflow">>, <<"reject-publish">>}
+ ]
+ ),
declare_queue(Ch, Name, [{<<"x-queue-type">>, longstr, <<"quorum">>}]),
bind(Ch, Name, Name),
@@ -296,14 +340,21 @@ quorum_queue_rejects(Config) ->
%% We expect m3 to be rejected and dropped.
?assertEqual(puback_timeout, util:publish_qos1_timeout(C, Name, <<"m3">>, 700)),
- ?assertMatch({#'basic.get_ok'{}, #amqp_msg{payload = <<"m1">>}},
- amqp_channel:call(Ch, #'basic.get'{queue = Name, no_ack = true})),
- ?assertMatch({#'basic.get_ok'{}, #amqp_msg{payload = <<"m2">>}},
- amqp_channel:call(Ch, #'basic.get'{queue = Name, no_ack = true})),
+ ?assertMatch(
+ {#'basic.get_ok'{}, #amqp_msg{payload = <<"m1">>}},
+ amqp_channel:call(Ch, #'basic.get'{queue = Name, no_ack = true})
+ ),
+ ?assertMatch(
+ {#'basic.get_ok'{}, #amqp_msg{payload = <<"m2">>}},
+ amqp_channel:call(Ch, #'basic.get'{queue = Name, no_ack = true})
+ ),
%% m3 is re-sent by emqtt.
- ?awaitMatch({#'basic.get_ok'{}, #amqp_msg{payload = <<"m3">>}},
- amqp_channel:call(Ch, #'basic.get'{queue = Name, no_ack = true}),
- 2000, 200),
+ ?awaitMatch(
+ {#'basic.get_ok'{}, #amqp_msg{payload = <<"m3">>}},
+ amqp_channel:call(Ch, #'basic.get'{queue = Name, no_ack = true}),
+ 2000,
+ 200
+ ),
ok = emqtt:disconnect(C),
delete_queue(Ch, Name),
@@ -339,40 +390,55 @@ publish_to_all_queue_types(Config, QoS) ->
NumMsgs = 2000,
C = connect(?FUNCTION_NAME, Config, [{retry_interval, 2}]),
- lists:foreach(fun(N) ->
- case emqtt:publish(C, Topic, integer_to_binary(N), QoS) of
- ok ->
- ok;
- {ok, _} ->
- ok;
- Other ->
- ct:fail("Failed to publish: ~p", [Other])
- end
- end, lists:seq(1, NumMsgs)),
-
- eventually(?_assert(
- begin
- L = rabbitmqctl_list(Config, 0, ["list_queues", "messages", "--no-table-headers"]),
- length(L) =:= 4 andalso
- lists:all(fun([Bin]) ->
- N = binary_to_integer(Bin),
- case QoS of
- qos0 ->
- N =:= NumMsgs;
- qos1 ->
- %% Allow for some duplicates when client resends
- %% a message that gets acked at roughly the same time.
- N >= NumMsgs andalso
- N < NumMsgs * 2
- end
- end, L)
- end), 2000, 10),
+ lists:foreach(
+ fun(N) ->
+ case emqtt:publish(C, Topic, integer_to_binary(N), QoS) of
+ ok ->
+ ok;
+ {ok, _} ->
+ ok;
+ Other ->
+ ct:fail("Failed to publish: ~p", [Other])
+ end
+ end,
+ lists:seq(1, NumMsgs)
+ ),
+
+ eventually(
+ ?_assert(
+ begin
+ L = rabbitmqctl_list(Config, 0, ["list_queues", "messages", "--no-table-headers"]),
+ length(L) =:= 4 andalso
+ lists:all(
+ fun([Bin]) ->
+ N = binary_to_integer(Bin),
+ case QoS of
+ qos0 ->
+ N =:= NumMsgs;
+ qos1 ->
+ %% Allow for some duplicates when client resends
+ %% a message that gets acked at roughly the same time.
+ N >= NumMsgs andalso
+ N < NumMsgs * 2
+ end
+ end,
+ L
+ )
+ end
+ ),
+ 2000,
+ 10
+ ),
delete_queue(Ch, [CQ, CMQ, QQ, SQ]),
ok = rabbit_ct_broker_helpers:clear_policy(Config, 0, CMQ),
ok = emqtt:disconnect(C),
- ?awaitMatch([],
- all_connection_pids(Config), 10_000, 1000).
+ ?awaitMatch(
+ [],
+ all_connection_pids(Config),
+ 10_000,
+ 1000
+ ).
flow_classic_mirrored_queue(Config) ->
QueueName = <<"flow">>,
@@ -386,8 +452,9 @@ flow_quorum_queue(Config) ->
flow_stream(Config) ->
flow(Config, {rabbit, stream_messages_soft_limit, 1}, <<"stream">>).
-flow(Config, {App, Par, Val}, QueueType)
- when is_binary(QueueType) ->
+flow(Config, {App, Par, Val}, QueueType) when
+ is_binary(QueueType)
+->
{ok, DefaultVal} = rpc(Config, application, get_env, [App, Par]),
Result = rpc_all(Config, application, set_env, [App, Par, Val]),
?assert(lists:all(fun(R) -> R =:= ok end, Result)),
@@ -398,27 +465,47 @@ flow(Config, {App, Par, Val}, QueueType)
bind(Ch, QueueName, Topic),
NumMsgs = 1000,
- C = connect(?FUNCTION_NAME, Config, [{retry_interval, 600},
- {max_inflight, NumMsgs}]),
+ C = connect(?FUNCTION_NAME, Config, [
+ {retry_interval, 600},
+ {max_inflight, NumMsgs}
+ ]),
TestPid = self(),
lists:foreach(
- fun(N) ->
- %% Publish async all messages at once to trigger flow control
- ok = emqtt:publish_async(C, Topic, integer_to_binary(N), qos1,
- {fun(N0, {ok, #{reason_code_name := success}}) ->
- TestPid ! {self(), N0}
- end, [N]})
- end, lists:seq(1, NumMsgs)),
+ fun(N) ->
+ %% Publish async all messages at once to trigger flow control
+ ok = emqtt:publish_async(
+ C,
+ Topic,
+ integer_to_binary(N),
+ qos1,
+ {
+ fun(N0, {ok, #{reason_code_name := success}}) ->
+ TestPid ! {self(), N0}
+ end,
+ [N]
+ }
+ )
+ end,
+ lists:seq(1, NumMsgs)
+ ),
ok = await_confirms_ordered(C, 1, NumMsgs),
- eventually(?_assertEqual(
- [[integer_to_binary(NumMsgs)]],
- rabbitmqctl_list(Config, 0, ["list_queues", "messages", "--no-table-headers"])
- ), 1000, 10),
+ eventually(
+ ?_assertEqual(
+ [[integer_to_binary(NumMsgs)]],
+ rabbitmqctl_list(Config, 0, ["list_queues", "messages", "--no-table-headers"])
+ ),
+ 1000,
+ 10
+ ),
delete_queue(Ch, QueueName),
ok = emqtt:disconnect(C),
- ?awaitMatch([],
- all_connection_pids(Config), 10_000, 1000),
+ ?awaitMatch(
+ [],
+ all_connection_pids(Config),
+ 10_000,
+ 1000
+ ),
Result = rpc_all(Config, application, set_env, [App, Par, DefaultVal]),
ok.
@@ -432,20 +519,27 @@ events(Config) ->
[E0, E1] = get_events(Server),
assert_event_type(user_authentication_success, E0),
- assert_event_prop([{name, <<"guest">>},
- {connection_type, network}],
- E0),
+ assert_event_prop(
+ [
+ {name, <<"guest">>},
+ {connection_type, network}
+ ],
+ E0
+ ),
assert_event_type(connection_created, E1),
[ConnectionPid] = all_connection_pids(Config),
- Proto = case ?config(websocket, Config) of
- true -> 'Web MQTT';
- false -> 'MQTT'
- end,
- ExpectedConnectionProps = [{protocol, {Proto, {3,1,1}}},
- {node, Server},
- {vhost, <<"/">>},
- {user, <<"guest">>},
- {pid, ConnectionPid}],
+ Proto =
+ case ?config(websocket, Config) of
+ true -> 'Web MQTT';
+ false -> 'MQTT'
+ end,
+ ExpectedConnectionProps = [
+ {protocol, {Proto, {3, 1, 1}}},
+ {node, Server},
+ {vhost, <<"/">>},
+ {user, <<"guest">>},
+ {pid, ConnectionPid}
+ ],
assert_event_prop(ExpectedConnectionProps, E1),
{ok, _, _} = emqtt:subscribe(C, <<"TopicA">>, qos0),
@@ -453,37 +547,50 @@ events(Config) ->
QueueNameBin = <<"mqtt-subscription-", ClientId/binary, "qos0">>,
QueueName = {resource, <<"/">>, queue, QueueNameBin},
[E2, E3 | E4] = get_events(Server),
- QueueType = case rabbit_ct_helpers:is_mixed_versions(Config) of
- false ->
- ?assertEqual([], E4),
- rabbit_mqtt_qos0_queue;
- true ->
- %% Feature flag rabbit_mqtt_qos0_queue is disabled.
- [ConsumerCreated] = E4,
- assert_event_type(consumer_created, ConsumerCreated),
- assert_event_prop([{queue, QueueName},
- {ack_required, false},
- {exclusive, false},
- {arguments, []}],
- ConsumerCreated),
- classic
- end,
+ QueueType =
+ case rabbit_ct_helpers:is_mixed_versions(Config) of
+ false ->
+ ?assertEqual([], E4),
+ rabbit_mqtt_qos0_queue;
+ true ->
+ %% Feature flag rabbit_mqtt_qos0_queue is disabled.
+ [ConsumerCreated] = E4,
+ assert_event_type(consumer_created, ConsumerCreated),
+ assert_event_prop(
+ [
+ {queue, QueueName},
+ {ack_required, false},
+ {exclusive, false},
+ {arguments, []}
+ ],
+ ConsumerCreated
+ ),
+ classic
+ end,
assert_event_type(queue_created, E2),
- assert_event_prop([{name, QueueName},
- {durable, true},
- {auto_delete, false},
- {exclusive, true},
- {type, QueueType},
- {arguments, []}],
- E2),
+ assert_event_prop(
+ [
+ {name, QueueName},
+ {durable, true},
+ {auto_delete, false},
+ {exclusive, true},
+ {type, QueueType},
+ {arguments, []}
+ ],
+ E2
+ ),
assert_event_type(binding_created, E3),
- assert_event_prop([{source_name, <<"amq.topic">>},
- {source_kind, exchange},
- {destination_name, QueueNameBin},
- {destination_kind, queue},
- {routing_key, <<"TopicA">>},
- {arguments, []}],
- E3),
+ assert_event_prop(
+ [
+ {source_name, <<"amq.topic">>},
+ {source_kind, exchange},
+ {destination_name, QueueNameBin},
+ {destination_kind, queue},
+ {routing_key, <<"TopicA">>},
+ {arguments, []}
+ ],
+ E3
+ ),
{ok, _, _} = emqtt:unsubscribe(C, <<"TopicA">>),
@@ -511,7 +618,9 @@ events(Config) ->
internal_event_handler(Config) ->
Server = get_node_config(Config, 0, nodename),
- ok = gen_event:call({rabbit_event, Server}, rabbit_mqtt_internal_event_handler, ignored_request, 1000).
+ ok = gen_event:call(
+ {rabbit_event, Server}, rabbit_mqtt_internal_event_handler, ignored_request, 1000
+ ).
global_counters_v3(Config) ->
global_counters(Config, v3).
@@ -539,62 +648,82 @@ global_counters(Config, ProtoVer) ->
ok = expect_publishes(C, Topic1, [<<"testm1">>]),
ok = expect_publishes(C, Topic2, [<<"testm2">>]),
- ?assertEqual(#{publishers => 1,
- consumers => 1,
- messages_confirmed_total => 2,
- messages_received_confirm_total => 2,
- messages_received_total => 5,
- messages_routed_total => 3,
- messages_unroutable_dropped_total => 1,
- messages_unroutable_returned_total => 1},
- get_global_counters(Config, ProtoVer)),
+ ?assertEqual(
+ #{
+ publishers => 1,
+ consumers => 1,
+ messages_confirmed_total => 2,
+ messages_received_confirm_total => 2,
+ messages_received_total => 5,
+ messages_routed_total => 3,
+ messages_unroutable_dropped_total => 1,
+ messages_unroutable_returned_total => 1
+ },
+ get_global_counters(Config, ProtoVer)
+ ),
case rabbit_ct_helpers:is_mixed_versions(Config) of
false ->
- ?assertEqual(#{messages_delivered_total => 2,
- messages_acknowledged_total => 1,
- messages_delivered_consume_auto_ack_total => 1,
- messages_delivered_consume_manual_ack_total => 1,
- messages_delivered_get_auto_ack_total => 0,
- messages_delivered_get_manual_ack_total => 0,
- messages_get_empty_total => 0,
- messages_redelivered_total => 0},
- get_global_counters(Config, ProtoVer, 0, [{queue_type, rabbit_classic_queue}])),
- ?assertEqual(#{messages_delivered_total => 1,
- messages_acknowledged_total => 0,
- messages_delivered_consume_auto_ack_total => 1,
- messages_delivered_consume_manual_ack_total => 0,
- messages_delivered_get_auto_ack_total => 0,
- messages_delivered_get_manual_ack_total => 0,
- messages_get_empty_total => 0,
- messages_redelivered_total => 0},
- get_global_counters(Config, ProtoVer, 0, [{queue_type, rabbit_mqtt_qos0_queue}]));
+ ?assertEqual(
+ #{
+ messages_delivered_total => 2,
+ messages_acknowledged_total => 1,
+ messages_delivered_consume_auto_ack_total => 1,
+ messages_delivered_consume_manual_ack_total => 1,
+ messages_delivered_get_auto_ack_total => 0,
+ messages_delivered_get_manual_ack_total => 0,
+ messages_get_empty_total => 0,
+ messages_redelivered_total => 0
+ },
+ get_global_counters(Config, ProtoVer, 0, [{queue_type, rabbit_classic_queue}])
+ ),
+ ?assertEqual(
+ #{
+ messages_delivered_total => 1,
+ messages_acknowledged_total => 0,
+ messages_delivered_consume_auto_ack_total => 1,
+ messages_delivered_consume_manual_ack_total => 0,
+ messages_delivered_get_auto_ack_total => 0,
+ messages_delivered_get_manual_ack_total => 0,
+ messages_get_empty_total => 0,
+ messages_redelivered_total => 0
+ },
+ get_global_counters(Config, ProtoVer, 0, [{queue_type, rabbit_mqtt_qos0_queue}])
+ );
true ->
%% Feature flag rabbit_mqtt_qos0_queue is disabled.
- ?assertEqual(#{messages_delivered_total => 3,
- messages_acknowledged_total => 1,
- messages_delivered_consume_auto_ack_total => 2,
- messages_delivered_consume_manual_ack_total => 1,
- messages_delivered_get_auto_ack_total => 0,
- messages_delivered_get_manual_ack_total => 0,
- messages_get_empty_total => 0,
- messages_redelivered_total => 0},
- get_global_counters(Config, ProtoVer, 0, [{queue_type, rabbit_classic_queue}]))
+ ?assertEqual(
+ #{
+ messages_delivered_total => 3,
+ messages_acknowledged_total => 1,
+ messages_delivered_consume_auto_ack_total => 2,
+ messages_delivered_consume_manual_ack_total => 1,
+ messages_delivered_get_auto_ack_total => 0,
+ messages_delivered_get_manual_ack_total => 0,
+ messages_get_empty_total => 0,
+ messages_redelivered_total => 0
+ },
+ get_global_counters(Config, ProtoVer, 0, [{queue_type, rabbit_classic_queue}])
+ )
end,
{ok, _, _} = emqtt:unsubscribe(C, Topic1),
?assertEqual(1, maps:get(consumers, get_global_counters(Config, ProtoVer))),
ok = emqtt:disconnect(C),
- ?assertEqual(#{publishers => 0,
- consumers => 0,
- messages_confirmed_total => 2,
- messages_received_confirm_total => 2,
- messages_received_total => 5,
- messages_routed_total => 3,
- messages_unroutable_dropped_total => 1,
- messages_unroutable_returned_total => 1},
- get_global_counters(Config, ProtoVer)).
+ ?assertEqual(
+ #{
+ publishers => 0,
+ consumers => 0,
+ messages_confirmed_total => 2,
+ messages_received_confirm_total => 2,
+ messages_received_total => 5,
+ messages_routed_total => 3,
+ messages_unroutable_dropped_total => 1,
+ messages_unroutable_returned_total => 1
+ },
+ get_global_counters(Config, ProtoVer)
+ ).
queue_down_qos1(Config) ->
{Conn1, Ch1} = rabbit_ct_client_helpers:open_connection_and_channel(Config, 1),
@@ -611,9 +740,14 @@ queue_down_qos1(Config) ->
ok = rabbit_ct_broker_helpers:start_node(Config, 1),
%% classic queue is up, therefore message should arrive
- eventually(?_assertEqual([[<<"1">>]],
- rabbitmqctl_list(Config, 1, ["list_queues", "messages", "--no-table-headers"])),
- 500, 20),
+ eventually(
+ ?_assertEqual(
+ [[<<"1">>]],
+ rabbitmqctl_list(Config, 1, ["list_queues", "messages", "--no-table-headers"])
+ ),
+ 500,
+ 20
+ ),
Ch0 = rabbit_ct_client_helpers:open_channel(Config, 0),
delete_queue(Ch0, CQ),
@@ -627,9 +761,16 @@ consuming_classic_mirrored_queue_down(Config) ->
ClientId = Topic = PolicyName = atom_to_binary(?FUNCTION_NAME),
ok = rabbit_ct_broker_helpers:set_policy(
- Config, Server1, PolicyName, <<".*">>, <<"queues">>,
- [{<<"ha-mode">>, <<"all">>},
- {<<"queue-master-locator">>, <<"client-local">>}]),
+ Config,
+ Server1,
+ PolicyName,
+ <<".*">>,
+ <<"queues">>,
+ [
+ {<<"ha-mode">>, <<"all">>},
+ {<<"queue-master-locator">>, <<"client-local">>}
+ ]
+ ),
%% Declare queue leader on Server1.
C1 = connect(ClientId, Config, Server1, [{clean_start, false}]),
@@ -653,12 +794,16 @@ consuming_classic_mirrored_queue_down(Config) ->
%% Cleanup
ok = emqtt:disconnect(C2),
ok = rabbit_ct_broker_helpers:start_node(Config, Server1),
- ?assertMatch([_Q],
- rpc(Config, Server1, rabbit_amqqueue, list, [])),
+ ?assertMatch(
+ [_Q],
+ rpc(Config, Server1, rabbit_amqqueue, list, [])
+ ),
C3 = connect(ClientId, Config, Server2, [{clean_start, true}]),
ok = emqtt:disconnect(C3),
- ?assertEqual([],
- rpc(Config, Server1, rabbit_amqqueue, list, [])),
+ ?assertEqual(
+ [],
+ rpc(Config, Server1, rabbit_amqqueue, list, [])
+ ),
ok = rabbit_ct_broker_helpers:clear_policy(Config, Server1, PolicyName).
%% Consuming classic queue on a different node goes down.
@@ -675,8 +820,10 @@ consuming_classic_queue_down(Config) ->
C2 = connect(ClientId, Config, Server3, [{clean_start, false}]),
ProtoVer = v4,
- ?assertMatch(#{consumers := 1},
- get_global_counters(Config, ProtoVer, Server3)),
+ ?assertMatch(
+ #{consumers := 1},
+ get_global_counters(Config, ProtoVer, Server3)
+ ),
%% Let's stop the queue leader node.
process_flag(trap_exit, true),
@@ -684,17 +831,24 @@ consuming_classic_queue_down(Config) ->
%% When the dedicated MQTT connection (non-mirrored classic) queue goes down, it is reasonable
%% that the server closes the MQTT connection because the MQTT client cannot consume anymore.
- eventually(?_assertMatch(#{consumers := 0},
- get_global_counters(Config, ProtoVer, Server3)),
- 1000, 5),
+ eventually(
+ ?_assertMatch(
+ #{consumers := 0},
+ get_global_counters(Config, ProtoVer, Server3)
+ ),
+ 1000,
+ 5
+ ),
await_exit(C2),
%% Cleanup
ok = rabbit_ct_broker_helpers:start_node(Config, Server1),
C3 = connect(ClientId, Config, Server3, [{clean_start, true}]),
ok = emqtt:disconnect(C3),
- ?assertEqual([],
- rpc(Config, Server1, rabbit_amqqueue, list, [])),
+ ?assertEqual(
+ [],
+ rpc(Config, Server1, rabbit_amqqueue, list, [])
+ ),
ok.
delete_create_queue(Config) ->
@@ -705,13 +859,13 @@ delete_create_queue(Config) ->
Topic = atom_to_binary(?FUNCTION_NAME),
DeclareQueues = fun() ->
- declare_queue(Ch, CQ1, []),
- bind(Ch, CQ1, Topic),
- declare_queue(Ch, CQ2, []),
- bind(Ch, CQ2, Topic),
- declare_queue(Ch, QQ, [{<<"x-queue-type">>, longstr, <<"quorum">>}]),
- bind(Ch, QQ, Topic)
- end,
+ declare_queue(Ch, CQ1, []),
+ bind(Ch, CQ1, Topic),
+ declare_queue(Ch, CQ2, []),
+ bind(Ch, CQ2, Topic),
+ declare_queue(Ch, QQ, [{<<"x-queue-type">>, longstr, <<"quorum">>}]),
+ bind(Ch, QQ, Topic)
+ end,
DeclareQueues(),
%% some large retry_interval to avoid re-sending
@@ -719,15 +873,26 @@ delete_create_queue(Config) ->
NumMsgs = 50,
TestPid = self(),
spawn(
- fun() ->
- lists:foreach(
+ fun() ->
+ lists:foreach(
fun(N) ->
- ok = emqtt:publish_async(C, Topic, integer_to_binary(N), qos1,
- {fun(N0, {ok, #{reason_code_name := success}}) ->
- TestPid ! {self(), N0}
- end, [N]})
- end, lists:seq(1, NumMsgs))
- end),
+ ok = emqtt:publish_async(
+ C,
+ Topic,
+ integer_to_binary(N),
+ qos1,
+ {
+ fun(N0, {ok, #{reason_code_name := success}}) ->
+ TestPid ! {self(), N0}
+ end,
+ [N]
+ }
+ )
+ end,
+ lists:seq(1, NumMsgs)
+ )
+ end
+ ),
%% Delete queues while sending to them.
%% We want to test the path where a queue is deleted while confirms are outstanding.
@@ -748,12 +913,23 @@ delete_create_queue(Config) ->
%% Sending a message to each of them should work.
{ok, _} = emqtt:publish(C, Topic, <<"m">>, qos1),
- eventually(?_assertEqual(lists:sort([[CQ1, <<"1">>],
- %% This queue should have all messages because we did not delete it.
- [CQ2, integer_to_binary(NumMsgs + 1)],
- [QQ, <<"1">>]]),
- lists:sort(rabbitmqctl_list(Config, 0, ["list_queues", "name", "messages", "--no-table-headers"]))),
- 1000, 10),
+ eventually(
+ ?_assertEqual(
+ lists:sort([
+ [CQ1, <<"1">>],
+ %% This queue should have all messages because we did not delete it.
+ [CQ2, integer_to_binary(NumMsgs + 1)],
+ [QQ, <<"1">>]
+ ]),
+ lists:sort(
+ rabbitmqctl_list(Config, 0, [
+ "list_queues", "name", "messages", "--no-table-headers"
+ ])
+ )
+ ),
+ 1000,
+ 10
+ ),
delete_queue(Ch, [CQ1, CQ2, QQ]),
ok = emqtt:disconnect(C).
@@ -766,13 +942,15 @@ subscription_ttl(Config) ->
ok = rpc(Config, application, set_env, [App, Par, TTL]),
C = connect(ClientId, Config, [{clean_start, false}]),
- {ok, _, [0, 1]} = emqtt:subscribe(C, [{<<"topic0">>, qos0},
- {<<"topic1">>, qos1}]),
+ {ok, _, [0, 1]} = emqtt:subscribe(C, [
+ {<<"topic0">>, qos0},
+ {<<"topic1">>, qos1}
+ ]),
ok = emqtt:disconnect(C),
?assertEqual(2, rpc(Config, rabbit_amqqueue, count, [])),
timer:sleep(TTL + 100),
- ?assertEqual(0, rpc(Config, rabbit_amqqueue, count, [])),
+ ?assertEqual(0, rpc(Config, rabbit_amqqueue, count, [])),
ok = rpc(Config, application, set_env, [App, Par, DefaultVal]).
@@ -789,28 +967,36 @@ non_clean_sess_reconnect(Config, SubscriptionQoS) ->
C1 = connect(ClientId, Config, [{clean_start, false}]),
{ok, _, _} = emqtt:subscribe(C1, Topic, SubscriptionQoS),
- ?assertMatch(#{consumers := 1},
- get_global_counters(Config, ProtoVer)),
+ ?assertMatch(
+ #{consumers := 1},
+ get_global_counters(Config, ProtoVer)
+ ),
ok = emqtt:disconnect(C1),
- ?assertMatch(#{consumers := 0},
- get_global_counters(Config, ProtoVer)),
+ ?assertMatch(
+ #{consumers := 0},
+ get_global_counters(Config, ProtoVer)
+ ),
timer:sleep(20),
ok = emqtt:publish(Pub, Topic, <<"msg-3-qos0">>, qos0),
{ok, _} = emqtt:publish(Pub, Topic, <<"msg-4-qos1">>, qos1),
C2 = connect(ClientId, Config, [{clean_start, false}]),
- ?assertMatch(#{consumers := 1},
- get_global_counters(Config, ProtoVer)),
+ ?assertMatch(
+ #{consumers := 1},
+ get_global_counters(Config, ProtoVer)
+ ),
ok = emqtt:publish(Pub, Topic, <<"msg-5-qos0">>, qos0),
{ok, _} = emqtt:publish(Pub, Topic, <<"msg-6-qos1">>, qos1),
%% shouldn't receive message after unsubscribe
{ok, _, _} = emqtt:unsubscribe(C2, Topic),
- ?assertMatch(#{consumers := 0},
- get_global_counters(Config, ProtoVer)),
+ ?assertMatch(
+ #{consumers := 0},
+ get_global_counters(Config, ProtoVer)
+ ),
{ok, _} = emqtt:publish(Pub, Topic, <<"msg-7-qos0">>, qos1),
%% "After the disconnection of a Session that had CleanSession set to 0, the Server MUST store
@@ -818,8 +1004,12 @@ non_clean_sess_reconnect(Config, SubscriptionQoS) ->
%% time of disconnection as part of the Session state [MQTT-3.1.2-5].
%% It MAY also store QoS 0 messages that meet the same criteria."
%% Starting with RabbitMQ v3.12 we store QoS 0 messages as well.
- ok = expect_publishes(C2, Topic, [<<"msg-3-qos0">>, <<"msg-4-qos1">>,
- <<"msg-5-qos0">>, <<"msg-6-qos1">>]),
+ ok = expect_publishes(C2, Topic, [
+ <<"msg-3-qos0">>,
+ <<"msg-4-qos1">>,
+ <<"msg-5-qos0">>,
+ <<"msg-6-qos1">>
+ ]),
{publish_not_received, <<"msg-7-qos0">>} = expect_publishes(C2, Topic, [<<"msg-7-qos0">>]),
ok = emqtt:disconnect(Pub),
@@ -837,19 +1027,25 @@ non_clean_sess_reconnect_qos0_and_qos1(Config) ->
C1 = connect(ClientId, Config, [{clean_start, false}]),
{ok, _, [1, 0]} = emqtt:subscribe(C1, [{Topic1, qos1}, {Topic0, qos0}]),
- ?assertMatch(#{consumers := 1},
- get_global_counters(Config, ProtoVer)),
+ ?assertMatch(
+ #{consumers := 1},
+ get_global_counters(Config, ProtoVer)
+ ),
ok = emqtt:disconnect(C1),
- ?assertMatch(#{consumers := 0},
- get_global_counters(Config, ProtoVer)),
+ ?assertMatch(
+ #{consumers := 0},
+ get_global_counters(Config, ProtoVer)
+ ),
{ok, _} = emqtt:publish(Pub, Topic0, <<"msg-0">>, qos1),
{ok, _} = emqtt:publish(Pub, Topic1, <<"msg-1">>, qos1),
C2 = connect(ClientId, Config, [{clean_start, false}]),
- ?assertMatch(#{consumers := 1},
- get_global_counters(Config, ProtoVer)),
+ ?assertMatch(
+ #{consumers := 1},
+ get_global_counters(Config, ProtoVer)
+ ),
ok = expect_publishes(C2, Topic0, [<<"msg-0">>]),
ok = expect_publishes(C2, Topic1, [<<"msg-1">>]),
@@ -863,8 +1059,10 @@ subscribe_same_topic_same_qos(Config) ->
C = connect(?FUNCTION_NAME, Config),
Topic = <<"a/b">>,
- {ok, _} = emqtt:publish(C, Topic, <<"retained">>, [{retain, true},
- {qos, 1}]),
+ {ok, _} = emqtt:publish(C, Topic, <<"retained">>, [
+ {retain, true},
+ {qos, 1}
+ ]),
%% Subscribe with QoS 0
{ok, _, [0]} = emqtt:subscribe(C, Topic, qos0),
{ok, _} = emqtt:publish(C, Topic, <<"msg1">>, qos1),
@@ -873,17 +1071,22 @@ subscribe_same_topic_same_qos(Config) ->
{ok, _} = emqtt:publish(C, Topic, <<"msg2">>, qos1),
%% "Any existing retained messages matching the Topic Filter MUST be re-sent" [MQTT-3.8.4-3]
- ok = expect_publishes(C, Topic, [<<"retained">>, <<"msg1">>,
- <<"retained">>, <<"msg2">>
- ]),
+ ok = expect_publishes(C, Topic, [
+ <<"retained">>,
+ <<"msg1">>,
+ <<"retained">>,
+ <<"msg2">>
+ ]),
ok = emqtt:disconnect(C).
subscribe_same_topic_different_qos(Config) ->
C = connect(?FUNCTION_NAME, Config, [{clean_start, false}]),
Topic = <<"b/c">>,
- {ok, _} = emqtt:publish(C, Topic, <<"retained">>, [{retain, true},
- {qos, 1}]),
+ {ok, _} = emqtt:publish(C, Topic, <<"retained">>, [
+ {retain, true},
+ {qos, 1}
+ ]),
%% Subscribe with QoS 0
{ok, _, [0]} = emqtt:subscribe(C, Topic, qos0),
{ok, _} = emqtt:publish(C, Topic, <<"msg1">>, qos1),
@@ -895,9 +1098,14 @@ subscribe_same_topic_different_qos(Config) ->
{ok, _} = emqtt:publish(C, Topic, <<"msg3">>, qos1),
%% "Any existing retained messages matching the Topic Filter MUST be re-sent" [MQTT-3.8.4-3]
- ok = expect_publishes(C, Topic, [<<"retained">>, <<"msg1">>,
- <<"retained">>, <<"msg2">>,
- <<"retained">>, <<"msg3">>]),
+ ok = expect_publishes(C, Topic, [
+ <<"retained">>,
+ <<"msg1">>,
+ <<"retained">>,
+ <<"msg2">>,
+ <<"retained">>,
+ <<"msg3">>
+ ]),
%% There should be exactly one consumer for each queue: qos0 and qos1
Consumers = rpc(Config, rabbit_amqqueue, consumers_all, [<<"/">>]),
@@ -910,9 +1118,13 @@ subscribe_same_topic_different_qos(Config) ->
subscribe_multiple(Config) ->
C = connect(?FUNCTION_NAME, Config),
%% Subscribe to multiple topics at once
- ?assertMatch({ok, _, [0, 1]},
- emqtt:subscribe(C, [{<<"topic0">>, qos0},
- {<<"topic1">>, qos1}])),
+ ?assertMatch(
+ {ok, _, [0, 1]},
+ emqtt:subscribe(C, [
+ {<<"topic0">>, qos0},
+ {<<"topic1">>, qos1}
+ ])
+ ),
ok = emqtt:disconnect(C).
large_message_mqtt_to_mqtt(Config) ->
@@ -934,10 +1146,14 @@ large_message_amqp_to_mqtt(Config) ->
Ch = rabbit_ct_client_helpers:open_channel(Config),
Payload0 = binary:copy(<<"x">>, 8_000_000),
Payload = <<Payload0/binary, "y">>,
- amqp_channel:call(Ch,
- #'basic.publish'{exchange = <<"amq.topic">>,
- routing_key = Topic},
- #amqp_msg{payload = Payload}),
+ amqp_channel:call(
+ Ch,
+ #'basic.publish'{
+ exchange = <<"amq.topic">>,
+ routing_key = Topic
+ },
+ #amqp_msg{payload = Payload}
+ ),
ok = expect_publishes(C, Topic, [Payload]),
ok = emqtt:disconnect(C).
@@ -947,10 +1163,14 @@ amqp_to_mqtt_qos0(Config) ->
{ok, _, [0]} = emqtt:subscribe(C, {Topic, qos0}),
Ch = rabbit_ct_client_helpers:open_channel(Config),
- amqp_channel:call(Ch,
- #'basic.publish'{exchange = <<"amq.topic">>,
- routing_key = Topic},
- #amqp_msg{payload = Payload}),
+ amqp_channel:call(
+ Ch,
+ #'basic.publish'{
+ exchange = <<"amq.topic">>,
+ routing_key = Topic
+ },
+ #amqp_msg{payload = Payload}
+ ),
ok = expect_publishes(C, Topic, [Payload]),
ok = emqtt:disconnect(C).
@@ -962,9 +1182,12 @@ many_qos1_messages(Config) ->
{ok, _, [1]} = emqtt:subscribe(C, {Topic, qos1}),
NumMsgs = 16#ffff + 100,
Payloads = lists:map(fun integer_to_binary/1, lists:seq(1, NumMsgs)),
- lists:foreach(fun(P) ->
- {ok, _} = emqtt:publish(C, Topic, P, qos1)
- end, Payloads),
+ lists:foreach(
+ fun(P) ->
+ {ok, _} = emqtt:publish(C, Topic, P, qos1)
+ end,
+ Payloads
+ ),
expect_publishes(C, Topic, Payloads),
ok = emqtt:disconnect(C).
@@ -995,15 +1218,21 @@ management_plugin_connection(Config) ->
C = connect(ClientId, Config, [{keepalive, KeepaliveSecs}]),
eventually(?_assertEqual(1, length(http_get(Config, "/connections"))), 1000, 10),
- [#{client_properties := #{client_id := ClientId},
- timeout := KeepaliveSecs,
- node := Node,
- name := ConnectionName}] = http_get(Config, "/connections"),
+ [
+ #{
+ client_properties := #{client_id := ClientId},
+ timeout := KeepaliveSecs,
+ node := Node,
+ name := ConnectionName
+ }
+ ] = http_get(Config, "/connections"),
process_flag(trap_exit, true),
- http_delete(Config,
- "/connections/" ++ binary_to_list(uri_string:quote((ConnectionName))),
- ?NO_CONTENT),
+ http_delete(
+ Config,
+ "/connections/" ++ binary_to_list(uri_string:quote((ConnectionName))),
+ ?NO_CONTENT
+ ),
await_exit(C),
?assertEqual([], http_get(Config, "/connections")),
eventually(?_assertEqual([], all_connection_pids(Config)), 500, 3).
@@ -1028,25 +1257,54 @@ cli_list_queues(Config) ->
{ok, _, _} = emqtt:subscribe(C, <<"a/b/c">>, qos0),
Qs = rabbit_ct_broker_helpers:rabbitmqctl_list(
- Config, 1,
- ["list_queues", "--no-table-headers",
- "type", "name", "state", "durable", "auto_delete",
- "arguments", "pid", "owner_pid", "messages", "exclusive_consumer_tag"
- ]),
- ExpectedQueueType = case rabbit_ct_helpers:is_mixed_versions(Config) of
- false ->
- <<"MQTT QoS 0">>;
- true ->
- <<"classic">>
- end,
- ?assertMatch([[ExpectedQueueType, <<"mqtt-subscription-cli_list_queuesqos0">>,
- <<"running">>, <<"true">>, <<"false">>, <<"[]">>, _, _, <<"0">>, <<"">>]],
- Qs),
-
- ?assertEqual([],
- rabbit_ct_broker_helpers:rabbitmqctl_list(
- Config, 1, ["list_queues", "--local", "--no-table-headers"])
- ),
+ Config,
+ 1,
+ [
+ "list_queues",
+ "--no-table-headers",
+ "type",
+ "name",
+ "state",
+ "durable",
+ "auto_delete",
+ "arguments",
+ "pid",
+ "owner_pid",
+ "messages",
+ "exclusive_consumer_tag"
+ ]
+ ),
+ ExpectedQueueType =
+ case rabbit_ct_helpers:is_mixed_versions(Config) of
+ false ->
+ <<"MQTT QoS 0">>;
+ true ->
+ <<"classic">>
+ end,
+ ?assertMatch(
+ [
+ [
+ ExpectedQueueType,
+ <<"mqtt-subscription-cli_list_queuesqos0">>,
+ <<"running">>,
+ <<"true">>,
+ <<"false">>,
+ <<"[]">>,
+ _,
+ _,
+ <<"0">>,
+ <<"">>
+ ]
+ ],
+ Qs
+ ),
+
+ ?assertEqual(
+ [],
+ rabbit_ct_broker_helpers:rabbitmqctl_list(
+ Config, 1, ["list_queues", "--local", "--no-table-headers"]
+ )
+ ),
ok = emqtt:disconnect(C).
@@ -1078,30 +1336,39 @@ keepalive(Config) ->
ProtoVer = v4,
WillTopic = <<"will/topic">>,
WillPayload = <<"will-payload">>,
- C1 = connect(?FUNCTION_NAME, Config, [{keepalive, KeepaliveSecs},
- {proto_ver, ProtoVer},
- {will_topic, WillTopic},
- {will_payload, WillPayload},
- {will_retain, true},
- {will_qos, 0}]),
+ C1 = connect(?FUNCTION_NAME, Config, [
+ {keepalive, KeepaliveSecs},
+ {proto_ver, ProtoVer},
+ {will_topic, WillTopic},
+ {will_payload, WillPayload},
+ {will_retain, true},
+ {will_qos, 0}
+ ]),
ok = emqtt:publish(C1, <<"ignored">>, <<"msg">>),
%% Connection should stay up when client sends PING requests.
timer:sleep(KeepaliveMs),
- ?assertMatch(#{publishers := 1},
- util:get_global_counters(Config, ProtoVer)),
+ ?assertMatch(
+ #{publishers := 1},
+ util:get_global_counters(Config, ProtoVer)
+ ),
%% Mock the server socket to not have received any bytes.
rabbit_ct_broker_helpers:setup_meck(Config),
Mod = rabbit_net,
ok = rpc(Config, meck, new, [Mod, [no_link, passthrough]]),
- ok = rpc(Config, meck, expect, [Mod, getstat, 2, {ok, [{recv_oct, 999}]} ]),
+ ok = rpc(Config, meck, expect, [Mod, getstat, 2, {ok, [{recv_oct, 999}]}]),
process_flag(trap_exit, true),
%% We expect the server to respect the keepalive closing the connection.
- eventually(?_assertMatch(#{publishers := 0},
- util:get_global_counters(Config, ProtoVer)),
- KeepaliveMs, 3 * KeepaliveSecs),
+ eventually(
+ ?_assertMatch(
+ #{publishers := 0},
+ util:get_global_counters(Config, ProtoVer)
+ ),
+ KeepaliveMs,
+ 3 * KeepaliveSecs
+ ),
await_exit(C1),
true = rpc(Config, meck, validate, [Mod]),
@@ -1109,12 +1376,16 @@ keepalive(Config) ->
C2 = connect(<<"client2">>, Config),
{ok, _, [0]} = emqtt:subscribe(C2, WillTopic),
- receive {publish, #{client_pid := C2,
- dup := false,
- qos := 0,
- retain := true,
- topic := WillTopic,
- payload := WillPayload}} -> ok
+ receive
+ {publish, #{
+ client_pid := C2,
+ dup := false,
+ qos := 0,
+ retain := true,
+ topic := WillTopic,
+ payload := WillPayload
+ }} ->
+ ok
after 3000 -> ct:fail("missing will")
end,
ok = emqtt:disconnect(C2).
@@ -1129,7 +1400,7 @@ keepalive_turned_off(Config) ->
rabbit_ct_broker_helpers:setup_meck(Config),
Mod = rabbit_net,
ok = rpc(Config, meck, new, [Mod, [no_link, passthrough]]),
- ok = rpc(Config, meck, expect, [Mod, getstat, 2, {ok, [{recv_oct, 999}]} ]),
+ ok = rpc(Config, meck, expect, [Mod, getstat, 2, {ok, [{recv_oct, 999}]}]),
rabbit_ct_helpers:consistently(?_assert(erlang:is_process_alive(C))),
@@ -1167,9 +1438,11 @@ block(Config) ->
%% Unblock
rpc(Config, vm_memory_monitor, set_vm_memory_high_watermark, [0.4]),
- ok = expect_publishes(C, Topic, [<<"Not blocked yet">>,
- <<"Now blocked">>,
- <<"Still blocked">>]),
+ ok = expect_publishes(C, Topic, [
+ <<"Not blocked yet">>,
+ <<"Now blocked">>,
+ <<"Still blocked">>
+ ]),
ok = emqtt:disconnect(C).
block_only_publisher(Config) ->
@@ -1237,7 +1510,9 @@ clean_session_disconnect_client(Config) ->
ok = emqtt:disconnect(C),
%% After terminating a clean session, we expect any session state to be cleaned up on the server.
- timer:sleep(200), %% Give some time to clean up exclusive classic queue.
+
+ %% Give some time to clean up exclusive classic queue.
+ timer:sleep(200),
L = rpc(Config, rabbit_amqqueue, list, []),
?assertEqual(0, length(L)).
@@ -1280,9 +1555,12 @@ trace(Config) ->
Ch = rabbit_ct_client_helpers:open_channel(Config),
declare_queue(Ch, TraceQ, []),
#'queue.bind_ok'{} = amqp_channel:call(
- Ch, #'queue.bind'{queue = TraceQ,
- exchange = <<"amq.rabbitmq.trace">>,
- routing_key = <<"#">>}),
+ Ch, #'queue.bind'{
+ queue = TraceQ,
+ exchange = <<"amq.rabbitmq.trace">>,
+ routing_key = <<"#">>
+ }
+ ),
%% We expect traced messages for connections created before and connections
%% created after tracing is enabled.
@@ -1294,45 +1572,65 @@ trace(Config) ->
{ok, _} = emqtt:publish(Pub, Topic, Payload, qos1),
ok = expect_publishes(Sub, Topic, [Payload]),
- {#'basic.get_ok'{routing_key = <<"publish.amq.topic">>},
- #amqp_msg{props = #'P_basic'{headers = PublishHeaders},
- payload = Payload}} =
- amqp_channel:call(Ch, #'basic.get'{queue = TraceQ, no_ack = false}),
- ?assertMatch(#{<<"exchange_name">> := <<"amq.topic">>,
- <<"routing_keys">> := [Topic],
- <<"connection">> := <<"127.0.0.1:", _/binary>>,
- <<"node">> := Server,
- <<"vhost">> := <<"/">>,
- <<"channel">> := 0,
- <<"user">> := <<"guest">>,
- <<"properties">> := #{<<"delivery_mode">> := 2,
- <<"headers">> := #{<<"x-mqtt-publish-qos">> := 1,
- <<"x-mqtt-dup">> := false}},
- <<"routed_queues">> := [<<"mqtt-subscription-trace_subscriberqos0">>]},
- rabbit_misc:amqp_table(PublishHeaders)),
-
- {#'basic.get_ok'{routing_key = <<"deliver.mqtt-subscription-trace_subscriberqos0">>},
- #amqp_msg{props = #'P_basic'{headers = DeliverHeaders},
- payload = Payload}} =
- amqp_channel:call(Ch, #'basic.get'{queue = TraceQ, no_ack = false}),
- ?assertMatch(#{<<"exchange_name">> := <<"amq.topic">>,
- <<"routing_keys">> := [Topic],
- <<"connection">> := <<"127.0.0.1:", _/binary>>,
- <<"node">> := Server,
- <<"vhost">> := <<"/">>,
- <<"channel">> := 0,
- <<"user">> := <<"guest">>,
- <<"properties">> := #{<<"delivery_mode">> := 2,
- <<"headers">> := #{<<"x-mqtt-publish-qos">> := 1,
- <<"x-mqtt-dup">> := false}},
- <<"redelivered">> := 0},
- rabbit_misc:amqp_table(DeliverHeaders)),
+ {#'basic.get_ok'{routing_key = <<"publish.amq.topic">>}, #amqp_msg{
+ props = #'P_basic'{headers = PublishHeaders},
+ payload = Payload
+ }} =
+ amqp_channel:call(Ch, #'basic.get'{queue = TraceQ, no_ack = false}),
+ ?assertMatch(
+ #{
+ <<"exchange_name">> := <<"amq.topic">>,
+ <<"routing_keys">> := [Topic],
+ <<"connection">> := <<"127.0.0.1:", _/binary>>,
+ <<"node">> := Server,
+ <<"vhost">> := <<"/">>,
+ <<"channel">> := 0,
+ <<"user">> := <<"guest">>,
+ <<"properties">> := #{
+ <<"delivery_mode">> := 2,
+ <<"headers">> := #{
+ <<"x-mqtt-publish-qos">> := 1,
+ <<"x-mqtt-dup">> := false
+ }
+ },
+ <<"routed_queues">> := [<<"mqtt-subscription-trace_subscriberqos0">>]
+ },
+ rabbit_misc:amqp_table(PublishHeaders)
+ ),
+
+ {#'basic.get_ok'{routing_key = <<"deliver.mqtt-subscription-trace_subscriberqos0">>}, #amqp_msg{
+ props = #'P_basic'{headers = DeliverHeaders},
+ payload = Payload
+ }} =
+ amqp_channel:call(Ch, #'basic.get'{queue = TraceQ, no_ack = false}),
+ ?assertMatch(
+ #{
+ <<"exchange_name">> := <<"amq.topic">>,
+ <<"routing_keys">> := [Topic],
+ <<"connection">> := <<"127.0.0.1:", _/binary>>,
+ <<"node">> := Server,
+ <<"vhost">> := <<"/">>,
+ <<"channel">> := 0,
+ <<"user">> := <<"guest">>,
+ <<"properties">> := #{
+ <<"delivery_mode">> := 2,
+ <<"headers">> := #{
+ <<"x-mqtt-publish-qos">> := 1,
+ <<"x-mqtt-dup">> := false
+ }
+ },
+ <<"redelivered">> := 0
+ },
+ rabbit_misc:amqp_table(DeliverHeaders)
+ ),
{ok, _} = rabbit_ct_broker_helpers:rabbitmqctl(Config, 0, ["trace_off"]),
{ok, _} = emqtt:publish(Pub, Topic, Payload, qos1),
ok = expect_publishes(Sub, Topic, [Payload]),
- ?assertMatch(#'basic.get_empty'{},
- amqp_channel:call(Ch, #'basic.get'{queue = TraceQ, no_ack = false})),
+ ?assertMatch(
+ #'basic.get_empty'{},
+ amqp_channel:call(Ch, #'basic.get'{queue = TraceQ, no_ack = false})
+ ),
delete_queue(Ch, TraceQ),
[ok = emqtt:disconnect(C) || C <- [Pub, Sub]].
@@ -1351,7 +1649,7 @@ await_confirms_ordered(From, N, To) ->
Got ->
ct:fail("Received unexpected message. Expected: ~p Got: ~p", [Expected, Got])
after 10_000 ->
- ct:fail("Did not receive expected message: ~p", [Expected])
+ ct:fail("Did not receive expected message: ~p", [Expected])
end.
await_confirms_unordered(_, 0) ->
@@ -1363,32 +1661,45 @@ await_confirms_unordered(From, Left) ->
Other ->
ct:fail("Received unexpected message: ~p", [Other])
after 10_000 ->
- ct:fail("~b confirms are missing", [Left])
+ ct:fail("~b confirms are missing", [Left])
end.
-declare_queue(Ch, QueueName, Args)
- when is_pid(Ch), is_binary(QueueName), is_list(Args) ->
+declare_queue(Ch, QueueName, Args) when
+ is_pid(Ch), is_binary(QueueName), is_list(Args)
+->
#'queue.declare_ok'{} = amqp_channel:call(
- Ch, #'queue.declare'{
- queue = QueueName,
- durable = true,
- arguments = Args}).
-
-delete_queue(Ch, QueueNames)
- when is_pid(Ch), is_list(QueueNames) ->
+ Ch, #'queue.declare'{
+ queue = QueueName,
+ durable = true,
+ arguments = Args
+ }
+ ).
+
+delete_queue(Ch, QueueNames) when
+ is_pid(Ch), is_list(QueueNames)
+->
lists:foreach(
- fun(Q) ->
- delete_queue(Ch, Q)
- end, QueueNames);
-delete_queue(Ch, QueueName)
- when is_pid(Ch), is_binary(QueueName) ->
+ fun(Q) ->
+ delete_queue(Ch, Q)
+ end,
+ QueueNames
+ );
+delete_queue(Ch, QueueName) when
+ is_pid(Ch), is_binary(QueueName)
+->
#'queue.delete_ok'{} = amqp_channel:call(
- Ch, #'queue.delete'{
- queue = QueueName}).
-
-bind(Ch, QueueName, Topic)
- when is_pid(Ch), is_binary(QueueName), is_binary(Topic) ->
+ Ch, #'queue.delete'{
+ queue = QueueName
+ }
+ ).
+
+bind(Ch, QueueName, Topic) when
+ is_pid(Ch), is_binary(QueueName), is_binary(Topic)
+->
#'queue.bind_ok'{} = amqp_channel:call(
- Ch, #'queue.bind'{queue = QueueName,
- exchange = <<"amq.topic">>,
- routing_key = Topic}).
+ Ch, #'queue.bind'{
+ queue = QueueName,
+ exchange = <<"amq.topic">>,
+ routing_key = Topic
+ }
+ ).
diff --git a/deps/rabbitmq_mqtt/test/util.erl b/deps/rabbitmq_mqtt/test/util.erl
index 44c684a6c5..38689b2110 100644
--- a/deps/rabbitmq_mqtt/test/util.erl
+++ b/deps/rabbitmq_mqtt/test/util.erl
@@ -4,46 +4,58 @@
-include_lib("rabbit_common/include/rabbit.hrl").
-include_lib("eunit/include/eunit.hrl").
--export([all_connection_pids/1,
- publish_qos1_timeout/4,
- sync_publish_result/3,
- get_global_counters/2,
- get_global_counters/3,
- get_global_counters/4,
- expect_publishes/3,
- connect/2,
- connect/3,
- connect/4,
- get_events/1,
- assert_event_type/2,
- assert_event_prop/2,
- await_exit/1,
- await_exit/2
- ]).
+-export([
+ all_connection_pids/1,
+ publish_qos1_timeout/4,
+ sync_publish_result/3,
+ get_global_counters/2,
+ get_global_counters/3,
+ get_global_counters/4,
+ expect_publishes/3,
+ connect/2,
+ connect/3,
+ connect/4,
+ get_events/1,
+ assert_event_type/2,
+ assert_event_prop/2,
+ await_exit/1,
+ await_exit/2
+]).
all_connection_pids(Config) ->
Nodes = rabbit_ct_broker_helpers:get_node_configs(Config, nodename),
Result = erpc:multicall(Nodes, rabbit_mqtt, local_connection_pids, [], 5000),
- lists:foldl(fun({ok, Pids}, Acc) ->
- Pids ++ Acc;
- (_, Acc) ->
- Acc
- end, [], Result).
+ lists:foldl(
+ fun
+ ({ok, Pids}, Acc) ->
+ Pids ++ Acc;
+ (_, Acc) ->
+ Acc
+ end,
+ [],
+ Result
+ ).
publish_qos1_timeout(Client, Topic, Payload, Timeout) ->
Mref = erlang:monitor(process, Client),
- ok = emqtt:publish_async(Client, Topic, #{}, Payload, [{qos, 1}], infinity,
- {fun ?MODULE:sync_publish_result/3, [self(), Mref]}),
+ ok = emqtt:publish_async(
+ Client,
+ Topic,
+ #{},
+ Payload,
+ [{qos, 1}],
+ infinity,
+ {fun ?MODULE:sync_publish_result/3, [self(), Mref]}
+ ),
receive
{Mref, Reply} ->
erlang:demonitor(Mref, [flush]),
Reply;
{'DOWN', Mref, process, Client, Reason} ->
ct:fail("client is down: ~tp", [Reason])
- after
- Timeout ->
- erlang:demonitor(Mref, [flush]),
- puback_timeout
+ after Timeout ->
+ erlang:demonitor(Mref, [flush]),
+ puback_timeout
end.
sync_publish_result(Caller, Mref, Result) ->
@@ -51,20 +63,27 @@ sync_publish_result(Caller, Mref, Result) ->
expect_publishes(_, _, []) ->
ok;
-expect_publishes(Client, Topic, [Payload|Rest])
- when is_pid(Client) ->
+expect_publishes(Client, Topic, [Payload | Rest]) when
+ is_pid(Client)
+->
receive
- {publish, #{client_pid := Client,
- topic := Topic,
- payload := Payload}} ->
+ {publish, #{
+ client_pid := Client,
+ topic := Topic,
+ payload := Payload
+ }} ->
expect_publishes(Client, Topic, Rest);
- {publish, #{client_pid := Client,
- topic := Topic,
- payload := Other}} ->
- ct:fail("Received unexpected PUBLISH payload. Expected: ~p Got: ~p",
- [Payload, Other])
+ {publish, #{
+ client_pid := Client,
+ topic := Topic,
+ payload := Other
+ }} ->
+ ct:fail(
+ "Received unexpected PUBLISH payload. Expected: ~p Got: ~p",
+ [Payload, Other]
+ )
after 3000 ->
- {publish_not_received, Payload}
+ {publish_not_received, Payload}
end.
get_global_counters(Config, ProtoVer) ->
@@ -78,11 +97,14 @@ get_global_counters(Config, v3, Node, QType) ->
get_global_counters(Config, v4, Node, QType) ->
get_global_counters(Config, ?MQTT_PROTO_V4, Node, QType);
get_global_counters(Config, Proto, Node, QType) ->
- maps:get([{protocol, Proto}] ++ QType,
- rabbit_ct_broker_helpers:rpc(Config, Node, rabbit_global_counters, overview, [])).
+ maps:get(
+ [{protocol, Proto}] ++ QType,
+ rabbit_ct_broker_helpers:rpc(Config, Node, rabbit_global_counters, overview, [])
+ ).
get_events(Node) ->
- timer:sleep(300), %% events are sent and processed asynchronously
+ %% events are sent and processed asynchronously
+ timer:sleep(300),
Result = gen_event:call({rabbit_event, Node}, event_recorder, take_state),
?assert(is_list(Result)),
Result.
@@ -92,24 +114,26 @@ assert_event_type(ExpectedType, #event{type = ActualType}) ->
assert_event_prop(ExpectedProp = {Key, _Value}, #event{props = Props}) ->
?assertEqual(ExpectedProp, lists:keyfind(Key, 1, Props));
-assert_event_prop(ExpectedProps, Event)
- when is_list(ExpectedProps) ->
- lists:foreach(fun(P) ->
- assert_event_prop(P, Event)
- end, ExpectedProps).
+assert_event_prop(ExpectedProps, Event) when
+ is_list(ExpectedProps)
+->
+ lists:foreach(
+ fun(P) ->
+ assert_event_prop(P, Event)
+ end,
+ ExpectedProps
+ ).
await_exit(Pid) ->
receive
{'EXIT', Pid, _} -> ok
- after
- 20_000 -> ct:fail({missing_exit, Pid})
+ after 20_000 -> ct:fail({missing_exit, Pid})
end.
await_exit(Pid, Reason) ->
receive
{'EXIT', Pid, Reason} -> ok
- after
- 20_000 -> ct:fail({missing_exit, Pid})
+ after 20_000 -> ct:fail({missing_exit, Pid})
end.
connect(ClientId, Config) ->
@@ -120,21 +144,27 @@ connect(ClientId, Config, AdditionalOpts) ->
connect(ClientId, Config, Node, AdditionalOpts) ->
{Port, WsOpts, Connect} =
- case rabbit_ct_helpers:get_config(Config, websocket, false) of
- false ->
- {rabbit_ct_broker_helpers:get_node_config(Config, Node, tcp_port_mqtt),
- [],
- fun emqtt:connect/1};
- true ->
- {rabbit_ct_broker_helpers:get_node_config(Config, Node, tcp_port_web_mqtt),
- [{ws_path, "/ws"}],
- fun emqtt:ws_connect/1}
- end,
- Options = [{host, "localhost"},
- {port, Port},
- {proto_ver, v4},
- {clientid, rabbit_data_coercion:to_binary(ClientId)}
- ] ++ WsOpts ++ AdditionalOpts,
+ case rabbit_ct_helpers:get_config(Config, websocket, false) of
+ false ->
+ {
+ rabbit_ct_broker_helpers:get_node_config(Config, Node, tcp_port_mqtt),
+ [],
+ fun emqtt:connect/1
+ };
+ true ->
+ {
+ rabbit_ct_broker_helpers:get_node_config(Config, Node, tcp_port_web_mqtt),
+ [{ws_path, "/ws"}],
+ fun emqtt:ws_connect/1
+ }
+ end,
+ Options =
+ [
+ {host, "localhost"},
+ {port, Port},
+ {proto_ver, v4},
+ {clientid, rabbit_data_coercion:to_binary(ClientId)}
+ ] ++ WsOpts ++ AdditionalOpts,
{ok, C} = emqtt:start_link(Options),
{ok, _Properties} = Connect(C),
C.
diff --git a/deps/rabbitmq_mqtt/test/util_SUITE.erl b/deps/rabbitmq_mqtt/test/util_SUITE.erl
index 3d058500ab..66b6f15c14 100644
--- a/deps/rabbitmq_mqtt/test/util_SUITE.erl
+++ b/deps/rabbitmq_mqtt/test/util_SUITE.erl
@@ -12,19 +12,18 @@
all() ->
[
- {group, tests}
+ {group, tests}
].
groups() ->
[
- {tests, [parallel], [
- coerce_exchange,
- coerce_vhost,
- coerce_default_user,
- coerce_default_pass,
- mqtt_amqp_topic_translation
- ]
- }
+ {tests, [parallel], [
+ coerce_exchange,
+ coerce_vhost,
+ coerce_default_user,
+ coerce_default_pass,
+ mqtt_amqp_topic_translation
+ ]}
].
suite() ->
diff --git a/deps/rabbitmq_web_mqtt/src/rabbit_web_mqtt_app.erl b/deps/rabbitmq_web_mqtt/src/rabbit_web_mqtt_app.erl
index 7070128205..7dafc013f2 100644
--- a/deps/rabbitmq_web_mqtt/src/rabbit_web_mqtt_app.erl
+++ b/deps/rabbitmq_web_mqtt/src/rabbit_web_mqtt_app.erl
@@ -49,7 +49,7 @@ init([]) -> {ok, {{one_for_one, 1, 5}, []}}.
-spec list_connections() -> [pid()].
list_connections() ->
PlainPids = connection_pids_of_protocol(?TCP_PROTOCOL),
- TLSPids = connection_pids_of_protocol(?TLS_PROTOCOL),
+ TLSPids = connection_pids_of_protocol(?TLS_PROTOCOL),
PlainPids ++ TLSPids.
%%
@@ -58,7 +58,8 @@ list_connections() ->
connection_pids_of_protocol(Protocol) ->
case rabbit_networking:ranch_ref_of_protocol(Protocol) of
- undefined -> [];
+ undefined ->
+ [];
AcceptorRef ->
lists:map(fun cowboy_ws_connection_pid/1, ranch:procs(AcceptorRef, connections))
end.
@@ -70,36 +71,38 @@ cowboy_ws_connection_pid(RanchConnPid) ->
Pid.
mqtt_init() ->
- CowboyOpts0 = maps:from_list(get_env(cowboy_opts, [])),
- CowboyWsOpts = maps:from_list(get_env(cowboy_ws_opts, [])),
- Routes = cowboy_router:compile([{'_', [
- {get_env(ws_path, "/ws"), rabbit_web_mqtt_handler, [{ws_opts, CowboyWsOpts}]}
- ]}]),
- CowboyOpts = CowboyOpts0#{
- env => #{dispatch => Routes},
- proxy_header => get_env(proxy_protocol, false),
- stream_handlers => [rabbit_web_mqtt_stream_handler, cowboy_stream_h]
- },
- case get_env(tcp_config, []) of
- [] -> ok;
- TCPConf0 -> start_tcp_listener(TCPConf0, CowboyOpts)
- end,
- case get_env(ssl_config, []) of
- [] -> ok;
- TLSConf0 -> start_tls_listener(TLSConf0, CowboyOpts)
- end,
- ok.
+ CowboyOpts0 = maps:from_list(get_env(cowboy_opts, [])),
+ CowboyWsOpts = maps:from_list(get_env(cowboy_ws_opts, [])),
+ Routes = cowboy_router:compile([
+ {'_', [
+ {get_env(ws_path, "/ws"), rabbit_web_mqtt_handler, [{ws_opts, CowboyWsOpts}]}
+ ]}
+ ]),
+ CowboyOpts = CowboyOpts0#{
+ env => #{dispatch => Routes},
+ proxy_header => get_env(proxy_protocol, false),
+ stream_handlers => [rabbit_web_mqtt_stream_handler, cowboy_stream_h]
+ },
+ case get_env(tcp_config, []) of
+ [] -> ok;
+ TCPConf0 -> start_tcp_listener(TCPConf0, CowboyOpts)
+ end,
+ case get_env(ssl_config, []) of
+ [] -> ok;
+ TLSConf0 -> start_tls_listener(TLSConf0, CowboyOpts)
+ end,
+ ok.
start_tcp_listener(TCPConf0, CowboyOpts) ->
{TCPConf, IpStr, Port} = get_tcp_conf(TCPConf0),
RanchRef = rabbit_networking:ranch_ref(TCPConf),
RanchTransportOpts =
- #{
- socket_opts => TCPConf,
- max_connections => get_max_connections(),
- num_acceptors => get_env(num_tcp_acceptors, 10),
- num_conns_sups => get_env(num_conns_sup, 1)
- },
+ #{
+ socket_opts => TCPConf,
+ max_connections => get_max_connections(),
+ num_acceptors => get_env(num_tcp_acceptors, 10),
+ num_conns_sups => get_env(num_conns_sup, 1)
+ },
case cowboy:start_clear(RanchRef, RanchTransportOpts, CowboyOpts) of
{ok, _} ->
ok;
@@ -107,25 +110,28 @@ start_tcp_listener(TCPConf0, CowboyOpts) ->
ok;
{error, ErrTCP} ->
rabbit_log:error(
- "Failed to start a WebSocket (HTTP) listener. Error: ~p, listener settings: ~p",
- [ErrTCP, TCPConf]),
+ "Failed to start a WebSocket (HTTP) listener. Error: ~p, listener settings: ~p",
+ [ErrTCP, TCPConf]
+ ),
throw(ErrTCP)
end,
listener_started(?TCP_PROTOCOL, TCPConf),
- rabbit_log:info("rabbit_web_mqtt: listening for HTTP connections on ~s:~w",
- [IpStr, Port]).
+ rabbit_log:info(
+ "rabbit_web_mqtt: listening for HTTP connections on ~s:~w",
+ [IpStr, Port]
+ ).
start_tls_listener(TLSConf0, CowboyOpts) ->
_ = rabbit_networking:ensure_ssl(),
{TLSConf, TLSIpStr, TLSPort} = get_tls_conf(TLSConf0),
RanchRef = rabbit_networking:ranch_ref(TLSConf),
RanchTransportOpts =
- #{
- socket_opts => TLSConf,
- max_connections => get_max_connections(),
- num_acceptors => get_env(num_ssl_acceptors, 10),
- num_conns_sups => get_env(num_conns_sup, 1)
- },
+ #{
+ socket_opts => TLSConf,
+ max_connections => get_max_connections(),
+ num_acceptors => get_env(num_ssl_acceptors, 10),
+ num_conns_sups => get_env(num_conns_sup, 1)
+ },
case cowboy:start_tls(RanchRef, RanchTransportOpts, CowboyOpts) of
{ok, _} ->
ok;
@@ -133,34 +139,45 @@ start_tls_listener(TLSConf0, CowboyOpts) ->
ok;
{error, ErrTLS} ->
rabbit_log:error(
- "Failed to start a TLS WebSocket (HTTPS) listener. Error: ~p, listener settings: ~p",
- [ErrTLS, TLSConf]),
+ "Failed to start a TLS WebSocket (HTTPS) listener. Error: ~p, listener settings: ~p",
+ [ErrTLS, TLSConf]
+ ),
throw(ErrTLS)
end,
listener_started(?TLS_PROTOCOL, TLSConf),
- rabbit_log:info("rabbit_web_mqtt: listening for HTTPS connections on ~s:~w",
- [TLSIpStr, TLSPort]).
+ rabbit_log:info(
+ "rabbit_web_mqtt: listening for HTTPS connections on ~s:~w",
+ [TLSIpStr, TLSPort]
+ ).
listener_started(Protocol, Listener) ->
Port = rabbit_misc:pget(port, Listener),
- [rabbit_networking:tcp_listener_started(Protocol, Listener,
- IPAddress, Port)
- || {IPAddress, _Port, _Family}
- <- rabbit_networking:tcp_listener_addresses(Port)],
+ [
+ rabbit_networking:tcp_listener_started(
+ Protocol,
+ Listener,
+ IPAddress,
+ Port
+ )
+ || {IPAddress, _Port, _Family} <-
+ rabbit_networking:tcp_listener_addresses(Port)
+ ],
ok.
get_tcp_conf(TCPConf0) ->
- TCPConf1 = case proplists:get_value(port, TCPConf0) of
- undefined -> [{port, 15675}|TCPConf0];
- _ -> TCPConf0
- end,
+ TCPConf1 =
+ case proplists:get_value(port, TCPConf0) of
+ undefined -> [{port, 15675} | TCPConf0];
+ _ -> TCPConf0
+ end,
get_ip_port(TCPConf1).
get_tls_conf(TLSConf0) ->
- TLSConf1 = case proplists:get_value(port, TLSConf0) of
- undefined -> [{port, 15675}|proplists:delete(port, TLSConf0)];
- _ -> TLSConf0
- end,
+ TLSConf1 =
+ case proplists:get_value(port, TLSConf0) of
+ undefined -> [{port, 15675} | proplists:delete(port, TLSConf0)];
+ _ -> TLSConf0
+ end,
get_ip_port(TLSConf1).
get_ip_port(Conf0) ->
@@ -177,7 +194,7 @@ normalize_ip(Ip) ->
Ip.
get_max_connections() ->
- get_env(max_connections, infinity).
+ get_env(max_connections, infinity).
get_env(Key, Default) ->
rabbit_misc:get_env(rabbitmq_web_mqtt, Key, Default).
diff --git a/deps/rabbitmq_web_mqtt/src/rabbit_web_mqtt_handler.erl b/deps/rabbitmq_web_mqtt/src/rabbit_web_mqtt_handler.erl
index 72f52b3977..2f017af696 100644
--- a/deps/rabbitmq_web_mqtt/src/rabbit_web_mqtt_handler.erl
+++ b/deps/rabbitmq_web_mqtt/src/rabbit_web_mqtt_handler.erl
@@ -24,23 +24,25 @@
-export([conserve_resources/3]).
%% cowboy_sub_protocol
--export([upgrade/4,
- upgrade/5,
- takeover/7]).
+-export([
+ upgrade/4,
+ upgrade/5,
+ takeover/7
+]).
-type option(T) :: undefined | T.
-record(state, {
- socket :: {rabbit_proxy_socket, any(), any()} | rabbit_net:socket(),
- parse_state = rabbit_mqtt_packet:initial_state() :: rabbit_mqtt_packet:state(),
- proc_state :: undefined | rabbit_mqtt_processor:state(),
- connection_state = running :: running | blocked,
- conserve = false :: boolean(),
- stats_timer :: option(rabbit_event:state()),
- keepalive = rabbit_mqtt_keepalive:init() :: rabbit_mqtt_keepalive:state(),
- conn_name :: option(binary()),
- received_connect_packet = false :: boolean()
- }).
+ socket :: {rabbit_proxy_socket, any(), any()} | rabbit_net:socket(),
+ parse_state = rabbit_mqtt_packet:initial_state() :: rabbit_mqtt_packet:state(),
+ proc_state :: undefined | rabbit_mqtt_processor:state(),
+ connection_state = running :: running | blocked,
+ conserve = false :: boolean(),
+ stats_timer :: option(rabbit_event:state()),
+ keepalive = rabbit_mqtt_keepalive:init() :: rabbit_mqtt_keepalive:state(),
+ conn_name :: option(binary()),
+ received_connect_packet = false :: boolean()
+}).
-type state() :: #state{}.
@@ -58,14 +60,22 @@ upgrade(Req, Env, Handler, HandlerState, Opts) ->
cowboy_websocket:upgrade(Req, Env, Handler, HandlerState, Opts).
takeover(Parent, Ref, Socket, Transport, Opts, Buffer, {Handler, {HandlerState, PeerAddr}}) ->
- Sock = case HandlerState#state.socket of
- undefined ->
- Socket;
- ProxyInfo ->
- {rabbit_proxy_socket, Socket, ProxyInfo}
- end,
- cowboy_websocket:takeover(Parent, Ref, Socket, Transport, Opts, Buffer,
- {Handler, {HandlerState#state{socket = Sock}, PeerAddr}}).
+ Sock =
+ case HandlerState#state.socket of
+ undefined ->
+ Socket;
+ ProxyInfo ->
+ {rabbit_proxy_socket, Socket, ProxyInfo}
+ end,
+ cowboy_websocket:takeover(
+ Parent,
+ Ref,
+ Socket,
+ Transport,
+ Opts,
+ Buffer,
+ {Handler, {HandlerState#state{socket = Sock}, PeerAddr}}
+ ).
%% cowboy_websocket
init(Req, Opts) ->
@@ -75,22 +85,20 @@ init(Req, Opts) ->
Protocol ->
{PeerAddr, _PeerPort} = maps:get(peer, Req),
WsOpts0 = proplists:get_value(ws_opts, Opts, #{}),
- WsOpts = maps:merge(#{compress => true}, WsOpts0),
+ WsOpts = maps:merge(#{compress => true}, WsOpts0),
case lists:member(<<"mqtt">>, Protocol) of
false ->
no_supported_sub_protocol(Protocol, Req);
true ->
{?MODULE,
- cowboy_req:set_resp_header(<<"sec-websocket-protocol">>, <<"mqtt">>, Req),
- {#state{socket = maps:get(proxy_header, Req, undefined)},
- PeerAddr},
- WsOpts}
+ cowboy_req:set_resp_header(<<"sec-websocket-protocol">>, <<"mqtt">>, Req),
+ {#state{socket = maps:get(proxy_header, Req, undefined)}, PeerAddr}, WsOpts}
end
end.
-spec websocket_init({state(), PeerAddr :: binary()}) ->
- {cowboy_websocket:commands(), state()} |
- {cowboy_websocket:commands(), state(), hibernate}.
+ {cowboy_websocket:commands(), state()}
+ | {cowboy_websocket:commands(), state(), hibernate}.
websocket_init({State0 = #state{socket = Sock}, PeerAddr}) ->
logger:set_process_metadata(#{domain => ?RMQLOG_DOMAIN_CONN ++ [web_mqtt]}),
ok = file_handle_cache:obtain(),
@@ -100,12 +108,15 @@ websocket_init({State0 = #state{socket = Sock}, PeerAddr}) ->
?LOG_INFO("Accepting Web MQTT connection ~s", [ConnName]),
_ = rabbit_alarm:register(self(), {?MODULE, conserve_resources, []}),
PState = rabbit_mqtt_processor:initial_state(
- rabbit_net:unwrap_socket(Sock),
- ConnName,
- fun send_reply/2,
- PeerAddr),
- State1 = State0#state{conn_name = ConnName,
- proc_state = PState},
+ rabbit_net:unwrap_socket(Sock),
+ ConnName,
+ fun send_reply/2,
+ PeerAddr
+ ),
+ State1 = State0#state{
+ conn_name = ConnName,
+ proc_state = PState
+ },
State = rabbit_event:init_stats_timer(State1, #state.stats_timer),
process_flag(trap_exit, true),
{[], State, hibernate};
@@ -113,24 +124,28 @@ websocket_init({State0 = #state{socket = Sock}, PeerAddr}) ->
{[{shutdown_reason, Reason}], State0}
end.
--spec conserve_resources(pid(),
- rabbit_alarm:resource_alarm_source(),
- rabbit_alarm:resource_alert()) -> ok.
+-spec conserve_resources(
+ pid(),
+ rabbit_alarm:resource_alarm_source(),
+ rabbit_alarm:resource_alert()
+) -> ok.
conserve_resources(Pid, _, {_, Conserve, _}) ->
Pid ! {conserve_resources, Conserve},
ok.
-spec websocket_handle(ping | pong | {text | binary | ping | pong, binary()}, State) ->
- {cowboy_websocket:commands(), State} |
- {cowboy_websocket:commands(), State, hibernate}.
+ {cowboy_websocket:commands(), State}
+ | {cowboy_websocket:commands(), State, hibernate}.
websocket_handle({binary, Data}, State) ->
handle_data(Data, State);
%% Silently ignore ping and pong frames as Cowboy will automatically reply to ping frames.
-websocket_handle({Ping, _}, State)
- when Ping =:= ping orelse Ping =:= pong ->
+websocket_handle({Ping, _}, State) when
+ Ping =:= ping orelse Ping =:= pong
+->
{[], State, hibernate};
-websocket_handle(Ping, State)
- when Ping =:= ping orelse Ping =:= pong ->
+websocket_handle(Ping, State) when
+ Ping =:= ping orelse Ping =:= pong
+->
{[], State, hibernate};
%% Log and close connection when receiving any other unexpected frames.
websocket_handle(Frame, State) ->
@@ -138,8 +153,8 @@ websocket_handle(Frame, State) ->
stop(State, ?CLOSE_UNACCEPTABLE_DATA_TYPE, <<"unexpected WebSocket frame">>).
-spec websocket_info(any(), State) ->
- {cowboy_websocket:commands(), State} |
- {cowboy_websocket:commands(), State, hibernate}.
+ {cowboy_websocket:commands(), State}
+ | {cowboy_websocket:commands(), State, hibernate}.
websocket_info({conserve_resources, Conserve}, State) ->
handle_credits(State#state{conserve = Conserve});
websocket_info({bump_credit, Msg}, State) ->
@@ -149,39 +164,66 @@ websocket_info({reply, Data}, State) ->
{[{binary, Data}], State, hibernate};
websocket_info({'EXIT', _, _}, State) ->
stop(State);
-websocket_info({'$gen_cast', QueueEvent = {queue_event, _, _}},
- State = #state{proc_state = PState0}) ->
+websocket_info(
+ {'$gen_cast', QueueEvent = {queue_event, _, _}},
+ State = #state{proc_state = PState0}
+) ->
case rabbit_mqtt_processor:handle_queue_event(QueueEvent, PState0) of
{ok, PState} ->
handle_credits(State#state{proc_state = PState});
{error, Reason, PState} ->
- ?LOG_ERROR("Web MQTT connection ~p failed to handle queue event: ~p",
- [State#state.conn_name, Reason]),
+ ?LOG_ERROR(
+ "Web MQTT connection ~p failed to handle queue event: ~p",
+ [State#state.conn_name, Reason]
+ ),
stop(State#state{proc_state = PState})
end;
-websocket_info({'$gen_cast', duplicate_id}, State = #state{ proc_state = ProcState,
- conn_name = ConnName }) ->
- ?LOG_WARNING("Web MQTT disconnecting a client with duplicate ID '~s' (~p)",
- [rabbit_mqtt_processor:info(client_id, ProcState), ConnName]),
+websocket_info(
+ {'$gen_cast', duplicate_id},
+ State = #state{
+ proc_state = ProcState,
+ conn_name = ConnName
+ }
+) ->
+ ?LOG_WARNING(
+ "Web MQTT disconnecting a client with duplicate ID '~s' (~p)",
+ [rabbit_mqtt_processor:info(client_id, ProcState), ConnName]
+ ),
stop(State);
-websocket_info({'$gen_cast', {close_connection, Reason}}, State = #state{ proc_state = ProcState,
- conn_name = ConnName }) ->
- ?LOG_WARNING("Web MQTT disconnecting client with ID '~s' (~p), reason: ~s",
- [rabbit_mqtt_processor:info(client_id, ProcState), ConnName, Reason]),
+websocket_info(
+ {'$gen_cast', {close_connection, Reason}},
+ State = #state{
+ proc_state = ProcState,
+ conn_name = ConnName
+ }
+) ->
+ ?LOG_WARNING(
+ "Web MQTT disconnecting client with ID '~s' (~p), reason: ~s",
+ [rabbit_mqtt_processor:info(client_id, ProcState), ConnName, Reason]
+ ),
stop(State);
websocket_info({'$gen_cast', {force_event_refresh, Ref}}, State0) ->
Infos = infos(?CREATION_EVENT_KEYS, State0),
rabbit_event:notify(connection_created, Infos, Ref),
State = rabbit_event:init_stats_timer(State0, #state.stats_timer),
{[], State, hibernate};
-websocket_info({'$gen_cast', refresh_config},
- State0 = #state{proc_state = PState0,
- conn_name = ConnName}) ->
+websocket_info(
+ {'$gen_cast', refresh_config},
+ State0 = #state{
+ proc_state = PState0,
+ conn_name = ConnName
+ }
+) ->
PState = rabbit_mqtt_processor:update_trace(ConnName, PState0),
State = State0#state{proc_state = PState},
{[], State, hibernate};
-websocket_info({keepalive, Req}, State = #state{keepalive = KState0,
- conn_name = ConnName}) ->
+websocket_info(
+ {keepalive, Req},
+ State = #state{
+ keepalive = KState0,
+ conn_name = ConnName
+ }
+) ->
case rabbit_mqtt_keepalive:handle(Req, KState0) of
{ok, KState} ->
{[], State#state{keepalive = KState}, hibernate};
@@ -189,18 +231,24 @@ websocket_info({keepalive, Req}, State = #state{keepalive = KState0,
?LOG_ERROR("keepalive timeout in Web MQTT connection ~p", [ConnName]),
stop(State, ?CLOSE_NORMAL, <<"MQTT keepalive timeout">>);
{error, Reason} ->
- ?LOG_ERROR("keepalive error in Web MQTT connection ~p: ~p",
- [ConnName, Reason]),
+ ?LOG_ERROR(
+ "keepalive error in Web MQTT connection ~p: ~p",
+ [ConnName, Reason]
+ ),
stop(State)
end;
websocket_info(emit_stats, State) ->
{[], emit_stats(State), hibernate};
-websocket_info({ra_event, _From, Evt},
- #state{proc_state = PState0} = State) ->
+websocket_info(
+ {ra_event, _From, Evt},
+ #state{proc_state = PState0} = State
+) ->
PState = rabbit_mqtt_processor:handle_ra_event(Evt, PState0),
{[], State#state{proc_state = PState}, hibernate};
-websocket_info({{'DOWN', _QName}, _MRef, process, _Pid, _Reason} = Evt,
- State = #state{proc_state = PState0}) ->
+websocket_info(
+ {{'DOWN', _QName}, _MRef, process, _Pid, _Reason} = Evt,
+ State = #state{proc_state = PState0}
+) ->
case rabbit_mqtt_processor:handle_down(Evt, PState0) of
{ok, PState} ->
handle_credits(State#state{proc_state = PState});
@@ -227,10 +275,16 @@ terminate(_Reason, _Req, #state{proc_state = undefined}) ->
ok;
terminate(Reason, Request, #state{} = State) ->
terminate(Reason, Request, {true, State});
-terminate(_Reason, _Request,
- {SendWill, #state{conn_name = ConnName,
- proc_state = PState,
- keepalive = KState} = State}) ->
+terminate(
+ _Reason,
+ _Request,
+ {SendWill,
+ #state{
+ conn_name = ConnName,
+ proc_state = PState,
+ keepalive = KState
+ } = State}
+) ->
?LOG_INFO("Web MQTT closing connection ~ts", [ConnName]),
maybe_emit_stats(State),
_ = rabbit_mqtt_keepalive:cancel_timer(KState),
@@ -252,29 +306,49 @@ handle_data(Data, State0 = #state{}) ->
Other
end.
-handle_data1(<<>>, State0 = #state{received_connect_packet = false,
- proc_state = PState,
- conn_name = ConnName}) ->
- ?LOG_INFO("Accepted web MQTT connection ~p (~s, client ID: ~s)",
- [self(), ConnName, rabbit_mqtt_processor:info(client_id, PState)]),
+handle_data1(
+ <<>>,
+ State0 = #state{
+ received_connect_packet = false,
+ proc_state = PState,
+ conn_name = ConnName
+ }
+) ->
+ ?LOG_INFO(
+ "Accepted web MQTT connection ~p (~s, client ID: ~s)",
+ [self(), ConnName, rabbit_mqtt_processor:info(client_id, PState)]
+ ),
State = State0#state{received_connect_packet = true},
{ok, ensure_stats_timer(control_throttle(State)), hibernate};
handle_data1(<<>>, State) ->
{ok, ensure_stats_timer(control_throttle(State)), hibernate};
-handle_data1(Data, State = #state{ parse_state = ParseState,
- proc_state = ProcState,
- conn_name = ConnName }) ->
+handle_data1(
+ Data,
+ State = #state{
+ parse_state = ParseState,
+ proc_state = ProcState,
+ conn_name = ConnName
+ }
+) ->
case parse(Data, ParseState) of
{more, ParseState1} ->
- {ok, ensure_stats_timer(control_throttle(
- State #state{ parse_state = ParseState1 })), hibernate};
+ {ok,
+ ensure_stats_timer(
+ control_throttle(
+ State#state{parse_state = ParseState1}
+ )
+ ),
+ hibernate};
{ok, Packet, Rest} ->
case rabbit_mqtt_processor:process_packet(Packet, ProcState) of
{ok, ProcState1} ->
handle_data1(
- Rest,
- State#state{parse_state = rabbit_mqtt_packet:initial_state(),
- proc_state = ProcState1});
+ Rest,
+ State#state{
+ parse_state = rabbit_mqtt_packet:initial_state(),
+ proc_state = ProcState1
+ }
+ );
{error, Reason, _} ->
stop_mqtt_protocol_error(State, Reason, ConnName);
{stop, disconnect, ProcState1} ->
@@ -289,9 +363,11 @@ parse(Data, ParseState) ->
rabbit_mqtt_packet:parse(Data, ParseState)
catch
_:Reason:Stacktrace ->
- ?LOG_ERROR("Web MQTT cannot parse a packet, reason: ~tp, stacktrace: ~tp, "
- "payload (first 100 bytes): ~tp",
- [Reason, Stacktrace, rabbit_mqtt_util:truncate_binary(Data, 100)]),
+ ?LOG_ERROR(
+ "Web MQTT cannot parse a packet, reason: ~tp, stacktrace: ~tp, "
+ "payload (first 100 bytes): ~tp",
+ [Reason, Stacktrace, rabbit_mqtt_util:truncate_binary(Data, 100)]
+ ),
{error, cannot_parse}
end.
@@ -308,26 +384,34 @@ stop(State, CloseCode, Error0) ->
handle_credits(State0) ->
State = #state{connection_state = CS} = control_throttle(State0),
- Active = case CS of
- running -> true;
- blocked -> false
- end,
+ Active =
+ case CS of
+ running -> true;
+ blocked -> false
+ end,
{[{active, Active}], State, hibernate}.
-control_throttle(State = #state{connection_state = ConnState,
- conserve = Conserve,
- received_connect_packet = Connected,
- proc_state = PState,
- keepalive = KState
- }) ->
+control_throttle(
+ State = #state{
+ connection_state = ConnState,
+ conserve = Conserve,
+ received_connect_packet = Connected,
+ proc_state = PState,
+ keepalive = KState
+ }
+) ->
Throttle = rabbit_mqtt_processor:throttle(Conserve, Connected, PState),
case {ConnState, Throttle} of
{running, true} ->
- State#state{connection_state = blocked,
- keepalive = rabbit_mqtt_keepalive:cancel_timer(KState)};
- {blocked,false} ->
- State#state{connection_state = running,
- keepalive = rabbit_mqtt_keepalive:start_timer(KState)};
+ State#state{
+ connection_state = blocked,
+ keepalive = rabbit_mqtt_keepalive:cancel_timer(KState)
+ };
+ {blocked, false} ->
+ State#state{
+ connection_state = running,
+ keepalive = rabbit_mqtt_keepalive:start_timer(KState)
+ };
{_, _} ->
State
end.
@@ -341,18 +425,23 @@ ensure_stats_timer(State) ->
maybe_emit_stats(#state{stats_timer = undefined}) ->
ok;
maybe_emit_stats(State) ->
- rabbit_event:if_enabled(State, #state.stats_timer,
- fun() -> emit_stats(State) end).
+ rabbit_event:if_enabled(
+ State,
+ #state.stats_timer,
+ fun() -> emit_stats(State) end
+ ).
-emit_stats(State=#state{received_connect_packet = false}) ->
+emit_stats(State = #state{received_connect_packet = false}) ->
%% Avoid emitting stats on terminate when the connection has not yet been
%% established, as this causes orphan entries on the stats database
rabbit_event:reset_stats_timer(State, #state.stats_timer);
emit_stats(State) ->
- [{_, Pid},
- {_, RecvOct},
- {_, SendOct},
- {_, Reductions}] = infos(?SIMPLE_METRICS, State),
+ [
+ {_, Pid},
+ {_, RecvOct},
+ {_, SendOct},
+ {_, Reductions}
+ ] = infos(?SIMPLE_METRICS, State),
Infos = infos(?OTHER_METRICS, State),
rabbit_core_metrics:connection_stats(Pid, Infos),
rabbit_core_metrics:connection_stats(Pid, RecvOct, SendOct, Reductions),
@@ -364,12 +453,13 @@ infos(Items, State) ->
i(pid, _) ->
self();
-i(SockStat, #state{socket = Sock})
- when SockStat =:= recv_oct;
- SockStat =:= recv_cnt;
- SockStat =:= send_oct;
- SockStat =:= send_cnt;
- SockStat =:= send_pend ->
+i(SockStat, #state{socket = Sock}) when
+ SockStat =:= recv_oct;
+ SockStat =:= recv_cnt;
+ SockStat =:= send_oct;
+ SockStat =:= send_cnt;
+ SockStat =:= send_pend
+->
case rabbit_net:getstat(Sock, [SockStat]) of
{ok, [{_, N}]} when is_number(N) ->
N;
@@ -383,22 +473,23 @@ i(garbage_collection, _) ->
rabbit_misc:get_gc_info(self());
i(protocol, #state{proc_state = PState}) ->
{?PROTO_FAMILY, rabbit_mqtt_processor:proto_version_tuple(PState)};
-i(SSL, #state{socket = Sock})
- when SSL =:= ssl;
- SSL =:= ssl_protocol;
- SSL =:= ssl_key_exchange;
- SSL =:= ssl_cipher;
- SSL =:= ssl_hash ->
- rabbit_ssl:info(SSL, {rabbit_net:unwrap_socket(Sock),
- rabbit_net:maybe_get_proxy_socket(Sock)});
+i(SSL, #state{socket = Sock}) when
+ SSL =:= ssl;
+ SSL =:= ssl_protocol;
+ SSL =:= ssl_key_exchange;
+ SSL =:= ssl_cipher;
+ SSL =:= ssl_hash
+->
+ rabbit_ssl:info(SSL, {rabbit_net:unwrap_socket(Sock), rabbit_net:maybe_get_proxy_socket(Sock)});
i(name, S) ->
i(conn_name, S);
i(conn_name, #state{conn_name = Val}) ->
Val;
-i(Cert, #state{socket = Sock})
- when Cert =:= peer_cert_issuer;
- Cert =:= peer_cert_subject;
- Cert =:= peer_cert_validity ->
+i(Cert, #state{socket = Sock}) when
+ Cert =:= peer_cert_issuer;
+ Cert =:= peer_cert_subject;
+ Cert =:= peer_cert_validity
+->
rabbit_ssl:cert_info(Cert, rabbit_net:unwrap_socket(Sock));
i(state, S) ->
i(connection_state, S);
diff --git a/deps/rabbitmq_web_mqtt/src/rabbit_web_mqtt_stream_handler.erl b/deps/rabbitmq_web_mqtt/src/rabbit_web_mqtt_stream_handler.erl
index 68d6ad2f29..b407623da6 100644
--- a/deps/rabbitmq_web_mqtt/src/rabbit_web_mqtt_stream_handler.erl
+++ b/deps/rabbitmq_web_mqtt/src/rabbit_web_mqtt_stream_handler.erl
@@ -14,7 +14,6 @@
-export([terminate/3]).
-export([early_error/5]).
-
-record(state, {next}).
init(StreamID, Req, Opts) ->
diff --git a/deps/rabbitmq_web_mqtt/test/config_schema_SUITE.erl b/deps/rabbitmq_web_mqtt/test/config_schema_SUITE.erl
index ec256f4421..2106d0a4ed 100644
--- a/deps/rabbitmq_web_mqtt/test/config_schema_SUITE.erl
+++ b/deps/rabbitmq_web_mqtt/test/config_schema_SUITE.erl
@@ -23,7 +23,6 @@ init_per_suite(Config) ->
Config1 = rabbit_ct_helpers:run_setup_steps(Config),
rabbit_ct_config_schema:init_schemas(rabbitmq_web_mqtt, Config1).
-
end_per_suite(Config) ->
rabbit_ct_helpers:run_teardown_steps(Config).
@@ -31,15 +30,19 @@ init_per_testcase(Testcase, Config) ->
rabbit_ct_helpers:testcase_started(Config, Testcase),
Config1 = rabbit_ct_helpers:set_config(Config, [
{rmq_nodename_suffix, Testcase}
- ]),
- rabbit_ct_helpers:run_steps(Config1,
- rabbit_ct_broker_helpers:setup_steps() ++
- rabbit_ct_client_helpers:setup_steps()).
+ ]),
+ rabbit_ct_helpers:run_steps(
+ Config1,
+ rabbit_ct_broker_helpers:setup_steps() ++
+ rabbit_ct_client_helpers:setup_steps()
+ ).
end_per_testcase(Testcase, Config) ->
- Config1 = rabbit_ct_helpers:run_steps(Config,
- rabbit_ct_client_helpers:teardown_steps() ++
- rabbit_ct_broker_helpers:teardown_steps()),
+ Config1 = rabbit_ct_helpers:run_steps(
+ Config,
+ rabbit_ct_client_helpers:teardown_steps() ++
+ rabbit_ct_broker_helpers:teardown_steps()
+ ),
rabbit_ct_helpers:testcase_finished(Config1, Testcase).
%% -------------------------------------------------------------------
@@ -47,9 +50,13 @@ end_per_testcase(Testcase, Config) ->
%% -------------------------------------------------------------------
run_snippets(Config) ->
- ok = rabbit_ct_broker_helpers:rpc(Config, 0,
- ?MODULE, run_snippets1, [Config]).
+ ok = rabbit_ct_broker_helpers:rpc(
+ Config,
+ 0,
+ ?MODULE,
+ run_snippets1,
+ [Config]
+ ).
run_snippets1(Config) ->
rabbit_ct_config_schema:run_snippets(Config).
-
diff --git a/deps/rabbitmq_web_mqtt/test/proxy_protocol_SUITE.erl b/deps/rabbitmq_web_mqtt/test/proxy_protocol_SUITE.erl
index 95cd3e5c44..c6512cd6b6 100644
--- a/deps/rabbitmq_web_mqtt/test/proxy_protocol_SUITE.erl
+++ b/deps/rabbitmq_web_mqtt/test/proxy_protocol_SUITE.erl
@@ -7,7 +7,6 @@
-module(proxy_protocol_SUITE).
-
-compile([export_all, nowarn_export_all]).
-include_lib("common_test/include/ct.hrl").
@@ -15,20 +14,24 @@
suite() ->
[
- %% If a test hangs, no need to wait for 30 minutes.
- {timetrap, {minutes, 2}}
+ %% If a test hangs, no need to wait for 30 minutes.
+ {timetrap, {minutes, 2}}
].
all() ->
- [{group, http_tests},
- {group, https_tests}].
+ [
+ {group, http_tests},
+ {group, https_tests}
+ ].
groups() ->
Tests = [
proxy_protocol
],
- [{https_tests, [], Tests},
- {http_tests, [], Tests}].
+ [
+ {https_tests, [], Tests},
+ {http_tests, [], Tests}
+ ].
init_per_suite(Config) ->
rabbit_ct_helpers:log_environment(),
@@ -38,22 +41,29 @@ end_per_suite(Config) ->
Config.
init_per_group(Group, Config) ->
- Protocol = case Group of
- http_tests -> "ws";
- https_tests -> "wss"
- end,
- Config1 = rabbit_ct_helpers:set_config(Config,
- [{rmq_nodename_suffix, ?MODULE},
- {protocol, Protocol},
- {rabbitmq_ct_tls_verify, verify_none},
- {rabbitmq_ct_tls_fail_if_no_peer_cert, false}]),
+ Protocol =
+ case Group of
+ http_tests -> "ws";
+ https_tests -> "wss"
+ end,
+ Config1 = rabbit_ct_helpers:set_config(
+ Config,
+ [
+ {rmq_nodename_suffix, ?MODULE},
+ {protocol, Protocol},
+ {rabbitmq_ct_tls_verify, verify_none},
+ {rabbitmq_ct_tls_fail_if_no_peer_cert, false}
+ ]
+ ),
rabbit_ct_helpers:run_setup_steps(
Config1,
- rabbit_ct_broker_helpers:setup_steps() ++ [
- fun configure_proxy_protocol/1,
- fun configure_ssl/1
- ]).
+ rabbit_ct_broker_helpers:setup_steps() ++
+ [
+ fun configure_proxy_protocol/1,
+ fun configure_ssl/1
+ ]
+ ).
configure_proxy_protocol(Config) ->
rabbit_ws_test_util:update_app_env(Config, proxy_protocol, true),
@@ -64,12 +74,16 @@ configure_ssl(Config) ->
RabbitAppConfig = proplists:get_value(rabbit, ErlangConfig, []),
RabbitSslConfig = proplists:get_value(ssl_options, RabbitAppConfig, []),
Port = rabbit_ct_broker_helpers:get_node_config(Config, 0, tcp_port_web_mqtt_tls),
- rabbit_ws_test_util:update_app_env(Config, ssl_config, [{port, Port} | lists:keydelete(port, 1, RabbitSslConfig)]),
+ rabbit_ws_test_util:update_app_env(Config, ssl_config, [
+ {port, Port} | lists:keydelete(port, 1, RabbitSslConfig)
+ ]),
Config.
end_per_group(_Group, Config) ->
- rabbit_ct_helpers:run_teardown_steps(Config,
- rabbit_ct_broker_helpers:teardown_steps()).
+ rabbit_ct_helpers:run_teardown_steps(
+ Config,
+ rabbit_ct_broker_helpers:teardown_steps()
+ ).
init_per_testcase(Testcase, Config) ->
rabbit_ct_helpers:testcase_started(Config, Testcase).
@@ -81,13 +95,23 @@ proxy_protocol(Config) ->
PortStr = rabbit_ws_test_util:get_web_mqtt_port_str(Config),
Protocol = ?config(protocol, Config),
- WS = rfc6455_client:new(Protocol ++ "://127.0.0.1:" ++ PortStr ++ "/ws", self(),
- undefined, ["mqtt"], "PROXY TCP4 192.168.1.1 192.168.1.2 80 81\r\n"),
+ WS = rfc6455_client:new(
+ Protocol ++ "://127.0.0.1:" ++ PortStr ++ "/ws",
+ self(),
+ undefined,
+ ["mqtt"],
+ "PROXY TCP4 192.168.1.1 192.168.1.2 80 81\r\n"
+ ),
{ok, _} = rfc6455_client:open(WS),
rfc6455_client:send_binary(WS, rabbit_ws_test_util:mqtt_3_1_1_connect_packet()),
{binary, _P} = rfc6455_client:recv(WS),
- ConnectionName = rabbit_ct_broker_helpers:rpc(Config, 0,
- ?MODULE, connection_name, []),
+ ConnectionName = rabbit_ct_broker_helpers:rpc(
+ Config,
+ 0,
+ ?MODULE,
+ connection_name,
+ []
+ ),
match = re:run(ConnectionName, <<"^192.168.1.1:80 -> 192.168.1.2:81$">>, [{capture, none}]),
{close, _} = rfc6455_client:close(WS),
ok.
diff --git a/deps/rabbitmq_web_mqtt/test/system_SUITE.erl b/deps/rabbitmq_web_mqtt/test/system_SUITE.erl
index 19b0f873c4..6642ae2d5a 100644
--- a/deps/rabbitmq_web_mqtt/test/system_SUITE.erl
+++ b/deps/rabbitmq_web_mqtt/test/system_SUITE.erl
@@ -18,13 +18,13 @@ all() ->
groups() ->
[
- {tests, [],
- [no_websocket_subprotocol
- ,unsupported_websocket_subprotocol
- ,unacceptable_data_type
- ,handle_invalid_packets
- ,duplicate_connect
- ]}
+ {tests, [], [
+ no_websocket_subprotocol,
+ unsupported_websocket_subprotocol,
+ unacceptable_data_type,
+ handle_invalid_packets,
+ duplicate_connect
+ ]}
].
suite() ->
@@ -35,15 +35,19 @@ init_per_suite(Config) ->
Config1 = rabbit_ct_helpers:set_config(Config, [
{rmq_nodename_suffix, ?MODULE},
{protocol, "ws"}
- ]),
- rabbit_ct_helpers:run_setup_steps(Config1,
- rabbit_ct_broker_helpers:setup_steps() ++
- rabbit_ct_client_helpers:setup_steps()).
+ ]),
+ rabbit_ct_helpers:run_setup_steps(
+ Config1,
+ rabbit_ct_broker_helpers:setup_steps() ++
+ rabbit_ct_client_helpers:setup_steps()
+ ).
end_per_suite(Config) ->
- rabbit_ct_helpers:run_teardown_steps(Config,
- rabbit_ct_client_helpers:teardown_steps() ++
- rabbit_ct_broker_helpers:teardown_steps()).
+ rabbit_ct_helpers:run_teardown_steps(
+ Config,
+ rabbit_ct_client_helpers:teardown_steps() ++
+ rabbit_ct_broker_helpers:teardown_steps()
+ ).
init_per_group(_, Config) ->
Config.
@@ -72,7 +76,9 @@ websocket_subprotocol(Config, SubProtocol) ->
PortStr = rabbit_ws_test_util:get_web_mqtt_port_str(Config),
WS = rfc6455_client:new("ws://localhost:" ++ PortStr ++ "/ws", self(), undefined, SubProtocol),
{_, [{http_response, Res}]} = rfc6455_client:open(WS),
- {'HTTP/1.1', 400, <<"Bad Request">>, _} = cow_http:parse_status_line(rabbit_data_coercion:to_binary(Res)),
+ {'HTTP/1.1', 400, <<"Bad Request">>, _} = cow_http:parse_status_line(
+ rabbit_data_coercion:to_binary(Res)
+ ),
rfc6455_client:send_binary(WS, rabbit_ws_test_util:mqtt_3_1_1_connect_packet()),
{close, _} = rfc6455_client:recv(WS, timer:seconds(1)).
@@ -110,7 +116,8 @@ duplicate_connect(Config) ->
process_flag(trap_exit, true),
rfc6455_client:send_binary(WS, rabbit_ws_test_util:mqtt_3_1_1_connect_packet()),
eventually(?_assertEqual(0, num_mqtt_connections(Config, 0))),
- receive {'EXIT', WS, _} -> ok
+ receive
+ {'EXIT', WS, _} -> ok
after 500 -> ct:fail("expected web socket to exit")
end.
diff --git a/rebar.config b/rebar.config
index 0590027d96..e291e7625a 100644
--- a/rebar.config
+++ b/rebar.config
@@ -14,3 +14,10 @@
inline_items => {when_under, 4}
}}
]}.
+
+{project_plugins, [erlfmt]}.
+{erlfmt, [
+ write,
+ {print_width, 100},
+ {files, "deps/{rabbitmq_mqtt,rabbitmq_web_mqtt}/{test,src}/*.erl"}
+]}.