From 209f23fa2f58e0240116b3e8e5be9cd54d34b569 Mon Sep 17 00:00:00 2001 From: Chunyi Lyu Date: Fri, 27 Jan 2023 18:25:57 +0000 Subject: [PATCH] Revert "Format MQTT code with `erlfmt`" --- .git-blame-ignore-revs | 1 - ...l.Commands.DecommissionMqttNodeCommand.erl | 45 +- ...tl.Commands.ListMqttConnectionsCommand.erl | 62 +- deps/rabbitmq_mqtt/src/mqtt_machine.erl | 284 +- deps/rabbitmq_mqtt/src/mqtt_machine_v0.erl | 112 +- deps/rabbitmq_mqtt/src/mqtt_node.erl | 119 +- deps/rabbitmq_mqtt/src/rabbit_mqtt.erl | 62 +- .../src/rabbit_mqtt_collector.erl | 35 +- .../src/rabbit_mqtt_confirms.erl | 71 +- deps/rabbitmq_mqtt/src/rabbit_mqtt_ff.erl | 20 +- .../rabbit_mqtt_internal_event_handler.erl | 4 +- .../src/rabbit_mqtt_keepalive.erl | 80 +- deps/rabbitmq_mqtt/src/rabbit_mqtt_packet.erl | 300 +- .../src/rabbit_mqtt_processor.erl | 2411 +++++++---------- .../src/rabbit_mqtt_qos0_queue.erl | 228 +- deps/rabbitmq_mqtt/src/rabbit_mqtt_reader.erl | 537 ++-- .../rabbit_mqtt_retained_msg_store_dets.erl | 52 +- .../rabbit_mqtt_retained_msg_store_ets.erl | 52 +- .../rabbit_mqtt_retained_msg_store_noop.erl | 12 +- .../src/rabbit_mqtt_retainer.erl | 64 +- .../src/rabbit_mqtt_retainer_sup.erl | 41 +- deps/rabbitmq_mqtt/src/rabbit_mqtt_sup.erl | 139 +- deps/rabbitmq_mqtt/src/rabbit_mqtt_util.erl | 98 +- deps/rabbitmq_mqtt/test/auth_SUITE.erl | 937 +++---- deps/rabbitmq_mqtt/test/cluster_SUITE.erl | 97 +- deps/rabbitmq_mqtt/test/command_SUITE.erl | 72 +- deps/rabbitmq_mqtt/test/config_SUITE.erl | 77 +- .../test/config_schema_SUITE.erl | 29 +- deps/rabbitmq_mqtt/test/event_recorder.erl | 9 +- deps/rabbitmq_mqtt/test/ff_SUITE.erl | 126 +- deps/rabbitmq_mqtt/test/java_SUITE.erl | 80 +- .../rabbitmq_mqtt/test/mqtt_machine_SUITE.erl | 90 +- deps/rabbitmq_mqtt/test/processor_SUITE.erl | 106 +- .../test/proxy_protocol_SUITE.erl | 63 +- .../test/rabbit_auth_backend_mqtt_mock.erl | 24 +- deps/rabbitmq_mqtt/test/reader_SUITE.erl | 186 +- deps/rabbitmq_mqtt/test/retainer_SUITE.erl | 84 +- deps/rabbitmq_mqtt/test/shared_SUITE.erl | 1233 ++++----- deps/rabbitmq_mqtt/test/util.erl | 160 +- deps/rabbitmq_mqtt/test/util_SUITE.erl | 17 +- .../src/rabbit_web_mqtt_app.erl | 125 +- .../src/rabbit_web_mqtt_handler.erl | 347 +-- .../src/rabbit_web_mqtt_stream_handler.erl | 1 + .../test/config_schema_SUITE.erl | 29 +- .../test/proxy_protocol_SUITE.erl | 78 +- deps/rabbitmq_web_mqtt/test/system_SUITE.erl | 39 +- rebar.config | 7 - 47 files changed, 3480 insertions(+), 5365 deletions(-) delete mode 100644 .git-blame-ignore-revs diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs deleted file mode 100644 index a751518a68..0000000000 --- a/.git-blame-ignore-revs +++ /dev/null @@ -1 +0,0 @@ -1de9fcf582def91d1cee6bea457dd24e8a53a431 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 a89dad8d16..fa8e09341c 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,20 +10,18 @@ -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() -> []. @@ -50,29 +48,20 @@ 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 f40868cd83..07265b56e1 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,22 +10,20 @@ -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]. @@ -39,14 +37,10 @@ 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) -> @@ -61,22 +55,19 @@ usage_additional() -> Prefix = <<" must be one of ">>, InfoItems = 'Elixir.Enum':join(lists:usort(?INFO_ITEMS), <<", ">>), [ - {<<"">>, <>} + {<<"">>, <>} ]. 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), @@ -87,8 +78,7 @@ run(Args, #{ [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 36c0c7e70f..20e69a7a2a 100644 --- a/deps/rabbitmq_mqtt/src/mqtt_machine.erl +++ b/deps/rabbitmq_mqtt/src/mqtt_machine.erl @@ -9,15 +9,13 @@ -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{}. @@ -26,10 +24,9 @@ -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; @@ -41,130 +38,93 @@ 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, - 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, +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, 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. @@ -173,69 +133,41 @@ 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 -) -> - {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) - }, +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)}, {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}}, []}. @@ -250,21 +182,17 @@ 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 702053b7b5..4b32ac88dd 100644 --- a/deps/rabbitmq_mqtt/src/mqtt_machine_v0.erl +++ b/deps/rabbitmq_mqtt/src/mqtt_machine_v0.erl @@ -9,12 +9,10 @@ -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{}. @@ -23,10 +21,9 @@ -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) -> @@ -38,60 +35,53 @@ 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. @@ -100,29 +90,25 @@ 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}}, []}. @@ -141,7 +127,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 8a3c850b78..a6442fa85b 100644 --- a/deps/rabbitmq_mqtt/src/mqtt_node.erl +++ b/deps/rabbitmq_mqtt/src/mqtt_node.erl @@ -6,15 +6,8 @@ %% -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). @@ -32,11 +25,8 @@ 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 @@ -50,41 +40,34 @@ 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() -> @@ -95,15 +78,14 @@ 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). @@ -121,13 +103,11 @@ 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 -> @@ -148,12 +128,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), @@ -171,13 +151,12 @@ 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 6e681ea3d2..5fb1861255 100644 --- a/deps/rabbitmq_mqtt/src/rabbit_mqtt.erl +++ b/deps/rabbitmq_mqtt/src/rabbit_mqtt.erl @@ -13,13 +13,11 @@ -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(), @@ -33,11 +31,10 @@ 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. @@ -55,15 +52,9 @@ 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. @@ -74,23 +65,17 @@ 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()]. @@ -101,12 +86,9 @@ 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 438b6cf9f1..5b5050d64e 100644 --- a/deps/rabbitmq_mqtt/src/rabbit_mqtt_collector.erl +++ b/deps/rabbitmq_mqtt/src/rabbit_mqtt_collector.erl @@ -9,13 +9,8 @@ -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()}. @@ -26,7 +21,7 @@ register(ClientId, Pid) -> case ra:members(NodeId) of {ok, _, Leader} -> register(Leader, ClientId, Pid); - _ = Error -> + _ = Error -> Error end; Leader -> @@ -65,31 +60,25 @@ 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 df56b14861..a31c94bd16 100644 --- a/deps/rabbitmq_mqtt/src/rabbit_mqtt_confirms.erl +++ b/deps/rabbitmq_mqtt/src/rabbit_mqtt_confirms.erl @@ -10,15 +10,13 @@ -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. @@ -41,32 +39,26 @@ 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}; @@ -79,31 +71,24 @@ reject(PktId, State0) when {[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 e132128527..2432cc2ac5 100644 --- a/deps/rabbitmq_mqtt/src/rabbit_mqtt_ff.erl +++ b/deps/rabbitmq_mqtt/src/rabbit_mqtt_ff.erl @@ -12,19 +12,17 @@ -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 c01f4cc5b3..711c065acc 100644 --- a/deps/rabbitmq_mqtt/src/rabbit_mqtt_internal_event_handler.erl +++ b/deps/rabbitmq_mqtt/src/rabbit_mqtt_internal_event_handler.erl @@ -28,9 +28,7 @@ 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 cadfcffdad..6b7b94b54c 100644 --- a/deps/rabbitmq_mqtt/src/rabbit_mqtt_keepalive.erl +++ b/deps/rabbitmq_mqtt/src/rabbit_mqtt_keepalive.erl @@ -1,26 +1,23 @@ -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() -> @@ -29,9 +26,8 @@ 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. @@ -40,28 +36,20 @@ start(Seconds, Sock) when 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}]} -> @@ -69,11 +57,8 @@ handle( {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. @@ -89,13 +74,10 @@ 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 bcd31d3529..ededed8c5b 100644 --- a/deps/rabbitmq_mqtt/src/rabbit_mqtt_packet.erl +++ b/deps/rabbitmq_mqtt/src/rabbit_mqtt_packet.erl @@ -24,148 +24,119 @@ 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(<>, 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, <>} -> {ProtoName, Rest1} = parse_utf(PacketBin), - <> = Rest1, - <> = Rest2, - {ClientId, Rest4} = parse_utf(Rest3), + <> = Rest1, + <> = 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, <>} -> {TopicName, Rest1} = parse_utf(PacketBin), - {PacketId, Payload} = - case Qos of - 0 -> - {undefined, Rest1}; - _ -> - <> = 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}; + _ -> <> = Rest1, + {M, R} + end, + wrap(Fixed, #mqtt_packet_publish { topic_name = TopicName, + packet_id = PacketId }, + Payload, Rest); {?PUBACK, <>} -> <> = PacketBin, - wrap(Fixed, #mqtt_packet_publish{packet_id = PacketId}, Rest); - {Subs, <>} when - Subs =:= ?SUBSCRIBE orelse Subs =:= ?UNSUBSCRIBE - -> + wrap(Fixed, #mqtt_packet_publish { packet_id = PacketId }, Rest); + {Subs, <>} + when Subs =:= ?SUBSCRIBE orelse Subs =:= ?UNSUBSCRIBE -> 1 = Qos, <> = 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( - <>, - Fixed, - Length - ) - end} + parse_packet(<>, + 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, <>} = 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}; @@ -187,109 +158,72 @@ 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 = <>, 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 = <>, - QosBin = - case Vsn of - ?MQTT_PROTO_V3 -> - <<<> || Q <- Qos>>; - ?MQTT_PROTO_V4 -> - %% Allow error code (0x80) in the MQTT SUBACK message. - <<<> || Q <- Qos>> - end, + QosBin = case Vsn of + ?MQTT_PROTO_V3 -> + << <> || Q <- Qos >>; + ?MQTT_PROTO_V4 -> + %% Allow error code (0x80) in the MQTT SUBACK message. + << <> || 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 -> <> - end, + PacketIdBin = case Qos of + 0 -> <<>>; + 1 -> <> + end, serialise_fixed(Fixed, <>, 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 = <>, 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), - [ - <>, - Payload - ]. + [<>, Payload]. serialise_utf(String) -> StringBin = unicode:characters_to_binary(String), @@ -302,9 +236,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 31c149cf5f..58bccaa2f5 100644 --- a/deps/rabbitmq_mqtt/src/rabbit_mqtt_processor.erl +++ b/deps/rabbitmq_mqtt/src/rabbit_mqtt_processor.erl @@ -8,31 +8,20 @@ %% 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"). @@ -44,57 +33,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(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(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(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{}. @@ -102,62 +91,50 @@ state(). initial_state(Socket, ConnectionName) -> {ok, {PeerAddr, _PeerPort}} = rabbit_net:peername(Socket), - initial_state( - Socket, - ConnectionName, - fun serialise_and_send_to_client/2, - PeerAddr - ). + 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() -) -> +-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 @@ -168,92 +145,68 @@ 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), @@ -269,219 +222,157 @@ process_request( _ -> 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). @@ -493,26 +384,16 @@ 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 -> @@ -531,21 +412,15 @@ check_credentials( {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 @@ -554,39 +429,28 @@ login( -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 @@ -594,10 +458,8 @@ register_client( {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 -> @@ -613,25 +475,17 @@ 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) -> @@ -653,18 +507,13 @@ 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) -> @@ -673,18 +522,11 @@ 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, @@ -702,16 +544,9 @@ handle_clean_sess( {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. @@ -736,8 +571,8 @@ handle_clean_sess( 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 @@ -757,10 +592,8 @@ 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). @@ -769,26 +602,23 @@ queue_name(QoS, #state{ 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 @@ -798,24 +628,21 @@ 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. @@ -829,124 +656,85 @@ 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 - }. +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 --> +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}; @@ -955,174 +743,134 @@ process_login( 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} - ] - ). + AuthResult, + [{name, Username}, + {connection_name, ConnName}, + {connection_type, network}]). -check_user_connection_limit(#{ - user := #user{username = Username}, - client_id := ClientId -}) -> +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. @@ -1135,12 +883,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}}; @@ -1151,13 +899,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)}; @@ -1170,26 +918,24 @@ 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. @@ -1208,13 +954,12 @@ 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) -> @@ -1227,14 +972,13 @@ 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), @@ -1242,15 +986,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; @@ -1267,10 +1011,8 @@ 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} -> @@ -1278,18 +1020,14 @@ 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 @@ -1299,34 +1037,29 @@ 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 -> @@ -1343,13 +1076,12 @@ 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]; @@ -1369,18 +1101,13 @@ 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 @@ -1391,35 +1118,30 @@ consume( %% 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 @@ -1441,37 +1163,25 @@ 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). @@ -1479,72 +1189,57 @@ 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, - %% GUID set in rabbit_classic_queue - id = <<>>, - is_persistent = Confirm - }, + exchange_name = ExchangeName, + routing_keys = [RoutingKey], + content = Content, + id = <<>>, %% GUID set in rabbit_classic_queue + 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), @@ -1555,61 +1250,39 @@ 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 @@ -1618,75 +1291,42 @@ process_routing_confirm( 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 - ). + #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 - } -}) -> +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()) -> @@ -1698,13 +1338,11 @@ 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()), @@ -1714,23 +1352,13 @@ 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), @@ -1740,32 +1368,24 @@ 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 @@ -1777,18 +1397,14 @@ 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 @@ -1801,21 +1417,19 @@ 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() -> @@ -1823,25 +1437,16 @@ 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 @@ -1850,13 +1455,9 @@ handle_ra_event( 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()), @@ -1872,13 +1473,9 @@ 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} -> @@ -1886,44 +1483,33 @@ handle_down( 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}, @@ -1933,10 +1519,8 @@ handle_queue_event( 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 -> @@ -1945,37 +1529,30 @@ handle_queue_event( 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. @@ -1991,10 +1568,8 @@ 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; @@ -2003,37 +1578,26 @@ 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 - ). + 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, +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), @@ -2051,33 +1615,28 @@ 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), @@ -2086,37 +1645,21 @@ 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. @@ -2131,21 +1674,13 @@ 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; @@ -2157,16 +1692,11 @@ trace_tap_out( 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 @@ -2181,75 +1711,57 @@ 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 @@ -2259,7 +1771,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(). @@ -2278,22 +1790,19 @@ 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; @@ -2303,19 +1812,23 @@ 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; @@ -2327,16 +1840,13 @@ 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) -> @@ -2350,73 +1860,47 @@ 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); @@ -2424,27 +1908,17 @@ message_acknowledged(QName, #state{ 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), @@ -2466,72 +1940,65 @@ 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, + #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 - }. + 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 1244c96483..deb0eff622 100644 --- a/deps/rabbitmq_mqtt/src/rabbit_mqtt_qos0_queue.erl +++ b/deps/rabbitmq_mqtt/src/rabbit_mqtt_qos0_queue.erl @@ -24,50 +24,42 @@ %% 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(). @@ -75,8 +67,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. @@ -84,29 +76,23 @@ 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), @@ -116,41 +102,35 @@ 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}. @@ -172,8 +152,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. @@ -181,24 +161,20 @@ 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()}. @@ -227,13 +203,11 @@ 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, _) -> @@ -275,26 +249,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 6f32816492..3949989230 100644 --- a/deps/rabbitmq_mqtt/src/rabbit_mqtt_reader.erl +++ b/deps/rabbitmq_mqtt/src/rabbit_mqtt_reader.erl @@ -14,20 +14,11 @@ -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([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([conserve_resources/3, + close_connection/2]). -export([info/2]). @@ -38,22 +29,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() -}). +-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{}. +-type(state() :: #state{}). %%---------------------------------------------------------------------------- @@ -61,11 +52,9 @@ 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. @@ -84,10 +73,8 @@ 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} -> @@ -97,17 +84,15 @@ 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); @@ -123,67 +108,51 @@ 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}. @@ -192,53 +161,49 @@ 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}; @@ -248,6 +213,7 @@ handle_info( {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}) -> @@ -258,47 +224,43 @@ 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), @@ -306,25 +268,36 @@ terminate( 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. @@ -336,80 +309,54 @@ 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]), @@ -418,11 +365,9 @@ process_received_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]), @@ -430,8 +375,8 @@ process_received_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) -> @@ -442,13 +387,9 @@ 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 @@ -456,77 +397,62 @@ network_error( 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}. + State#state{ await_recv = true }. -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) - }; + 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), @@ -539,13 +465,12 @@ 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; @@ -569,19 +494,17 @@ 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); @@ -591,48 +514,40 @@ 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 bfaa0cd146..8dbd06a440 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,57 +13,53 @@ -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 bbc7a77390..3a0a7384db 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,51 +13,49 @@ -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 5f6f0ef56b..1e62ed5abe 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 8df5d80ff7..1c632eb597 100644 --- a/deps/rabbitmq_mqtt/src/rabbit_mqtt_retainer.erl +++ b/deps/rabbitmq_mqtt/src/rabbit_mqtt_retainer.erl @@ -12,23 +12,15 @@ -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}). %%---------------------------------------------------------------------------- @@ -54,19 +46,12 @@ 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(). @@ -78,33 +63,26 @@ 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 f2b2a38867..eadaab7ca2 100644 --- a/deps/rabbitmq_mqtt/src/rabbit_mqtt_retainer_sup.erl +++ b/deps/rabbitmq_mqtt/src/rabbit_mqtt_retainer_sup.erl @@ -8,13 +8,8 @@ -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(). @@ -24,13 +19,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( @@ -46,20 +41,18 @@ 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 5aef65bc2d..a04ef73cb9 100644 --- a/deps/rabbitmq_mqtt/src/rabbit_mqtt_sup.erl +++ b/deps/rabbitmq_mqtt/src/rabbit_mqtt_sup.erl @@ -23,61 +23,52 @@ 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() -> @@ -98,33 +89,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 89428c5965..d098d3ff93 100644 --- a/deps/rabbitmq_mqtt/src/rabbit_mqtt_util.erl +++ b/deps/rabbitmq_mqtt/src/rabbit_mqtt_util.erl @@ -11,21 +11,20 @@ -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). @@ -73,8 +72,7 @@ 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 -> @@ -104,13 +102,12 @@ 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; @@ -145,11 +142,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; @@ -164,11 +161,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(). @@ -177,9 +174,8 @@ 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 @@ -187,12 +183,10 @@ register_clientid(Vhost, ClientId) when %% 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. @@ -200,24 +194,18 @@ 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 f2ad72f857..63ac362842 100644 --- a/deps/rabbitmq_mqtt/test/auth_SUITE.erl +++ b/deps/rabbitmq_mqtt/test/auth_SUITE.erl @@ -5,10 +5,8 @@ %% 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"). @@ -16,78 +14,79 @@ %% 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) -> @@ -101,29 +100,19 @@ 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, [ @@ -132,77 +121,56 @@ 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) -> @@ -223,10 +191,8 @@ 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), @@ -239,9 +205,7 @@ 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), @@ -251,12 +215,9 @@ 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); @@ -284,9 +245,7 @@ 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). @@ -309,9 +268,7 @@ 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) -> @@ -319,29 +276,24 @@ 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), @@ -349,19 +301,15 @@ end_per_testcase(TestCase, Config) when 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), @@ -382,31 +330,24 @@ 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), <<>>), @@ -417,7 +358,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). @@ -425,6 +366,7 @@ 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). @@ -437,38 +379,31 @@ 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). @@ -485,8 +420,7 @@ 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 @@ -516,92 +450,68 @@ 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)), @@ -621,13 +531,12 @@ 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) -> @@ -635,45 +544,36 @@ 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, <<".*">>, <<".*">>, <<".*">>), @@ -683,25 +583,20 @@ 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), @@ -710,114 +605,82 @@ 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) -> @@ -826,20 +689,13 @@ 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) -> @@ -853,21 +709,15 @@ 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) -> @@ -879,24 +729,15 @@ 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) -> @@ -913,107 +754,65 @@ 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) -> @@ -1021,25 +820,20 @@ 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, []), @@ -1087,14 +881,10 @@ 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) -> @@ -1116,47 +906,42 @@ 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 15f4e92ddc..0ac2434ea2 100644 --- a/deps/rabbitmq_mqtt/test/cluster_SUITE.erl +++ b/deps/rabbitmq_mqtt/test/cluster_SUITE.erl @@ -9,42 +9,36 @@ -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(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 - ] -). +-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} -]). +-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() -> @@ -56,12 +50,11 @@ 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(), @@ -72,8 +65,7 @@ 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. @@ -83,25 +75,19 @@ 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). %% ------------------------------------------------------------------- @@ -179,15 +165,14 @@ 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 1f3831f94e..76421287d3 100644 --- a/deps/rabbitmq_mqtt/test/command_SUITE.erl +++ b/deps/rabbitmq_mqtt/test/command_SUITE.erl @@ -4,6 +4,7 @@ %% %% Copyright (c) 2007-2023 VMware, Inc. or its affiliates. All rights reserved. + -module(command_SUITE). -compile([export_all, nowarn_export_all]). @@ -16,45 +17,39 @@ 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. @@ -78,6 +73,7 @@ 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}, @@ -95,27 +91,18 @@ 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), @@ -123,19 +110,14 @@ 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 37cb23f43a..b2b04b52e2 100644 --- a/deps/rabbitmq_mqtt/test/config_SUITE.erl +++ b/deps/rabbitmq_mqtt/test/config_SUITE.erl @@ -5,10 +5,8 @@ %% 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"). @@ -16,16 +14,17 @@ all() -> [ - {group, mnesia} + {group, mnesia} ]. groups() -> [ - {mnesia, [shuffle], [ - rabbitmq_default, - environment_set, - flag_set - ]} + {mnesia, [shuffle], + [ + rabbitmq_default, + environment_set, + flag_set + ]} ]. suite() -> @@ -46,12 +45,8 @@ 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], @@ -60,19 +55,17 @@ 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 @@ -81,33 +74,21 @@ 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 b62ca26185..c3f3d867c4 100644 --- a/deps/rabbitmq_mqtt/test/config_schema_SUITE.erl +++ b/deps/rabbitmq_mqtt/test/config_schema_SUITE.erl @@ -23,6 +23,7 @@ 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). @@ -30,19 +31,15 @@ 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). %% ------------------------------------------------------------------- @@ -50,13 +47,9 @@ 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 4c2af35437..cd495f9427 100644 --- a/deps/rabbitmq_mqtt/test/event_recorder.erl +++ b/deps/rabbitmq_mqtt/test/event_recorder.erl @@ -15,11 +15,10 @@ 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 5e002f808f..a7c528c640 100644 --- a/deps/rabbitmq_mqtt/test/ff_SUITE.erl +++ b/deps/rabbitmq_mqtt/test/ff_SUITE.erl @@ -13,31 +13,27 @@ -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) -> @@ -48,36 +44,26 @@ 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) -> @@ -90,27 +76,16 @@ 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]) - ) - ), + ?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) - ), + ?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)), @@ -124,30 +99,20 @@ 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), @@ -155,22 +120,13 @@ 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 4e3fdeed29..5155f1ba77 100644 --- a/deps/rabbitmq_mqtt/test/java_SUITE.erl +++ b/deps/rabbitmq_mqtt/test/java_SUITE.erl @@ -12,25 +12,24 @@ -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() -> @@ -53,20 +52,16 @@ 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. @@ -79,38 +74,25 @@ 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 %% ------------------------------------------------------------------- @@ -140,5 +122,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 ebf773aa44..274877cdc8 100644 --- a/deps/rabbitmq_mqtt/test/mqtt_machine_SUITE.erl +++ b/deps/rabbitmq_mqtt/test/mqtt_machine_SUITE.erl @@ -12,19 +12,20 @@ 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) -> @@ -52,16 +53,13 @@ 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), @@ -76,63 +74,41 @@ 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 1078462ad8..15ae0dd537 100644 --- a/deps/rabbitmq_mqtt/test/processor_SUITE.erl +++ b/deps/rabbitmq_mqtt/test/processor_SUITE.erl @@ -4,6 +4,7 @@ %% %% Copyright (c) 2007-2023 VMware, Inc. or its affiliates. All rights reserved. + -module(processor_SUITE). -compile([export_all, nowarn_export_all]). @@ -13,17 +14,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() -> @@ -41,50 +42,35 @@ 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(), @@ -97,35 +83,27 @@ 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 @@ -143,9 +121,7 @@ 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 @@ -162,50 +138,42 @@ 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, [ @@ -217,15 +185,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 6499b9aaea..551b14c865 100644 --- a/deps/rabbitmq_mqtt/test/proxy_protocol_SUITE.erl +++ b/deps/rabbitmq_mqtt/test/proxy_protocol_SUITE.erl @@ -34,26 +34,21 @@ 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. @@ -66,11 +61,8 @@ 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), @@ -83,11 +75,8 @@ 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()), @@ -107,5 +96,29 @@ 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 ad1bfe1e58..69fea6c221 100644 --- a/deps/rabbitmq_mqtt/test/rabbit_auth_backend_mqtt_mock.erl +++ b/deps/rabbitmq_mqtt/test/rabbit_auth_backend_mqtt_mock.erl @@ -13,16 +13,11 @@ -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]), @@ -31,13 +26,12 @@ 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 5a96612231..4c65e20e47 100644 --- a/deps/rabbitmq_mqtt/test/reader_SUITE.erl +++ b/deps/rabbitmq_mqtt/test/reader_SUITE.erl @@ -5,45 +5,42 @@ %% 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() -> @@ -54,36 +51,28 @@ 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. @@ -97,6 +86,7 @@ init_per_testcase(Testcase, Config) -> end_per_testcase(Testcase, Config) -> rabbit_ct_helpers:testcase_finished(Config, Testcase). + %% ------------------------------------------------------------------- %% Testsuite cases %% ------------------------------------------------------------------- @@ -110,13 +100,11 @@ 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), @@ -124,7 +112,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, []), @@ -142,7 +130,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)), @@ -182,12 +170,8 @@ 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) -> @@ -232,41 +216,32 @@ 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), @@ -277,13 +252,9 @@ 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, []). @@ -295,12 +266,8 @@ 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), @@ -312,12 +279,9 @@ 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), @@ -354,11 +318,9 @@ 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 eb3f8dedd5..3a2585e8fe 100644 --- a/deps/rabbitmq_mqtt/test/retainer_SUITE.erl +++ b/deps/rabbitmq_mqtt/test/retainer_SUITE.erl @@ -8,31 +8,29 @@ -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() -> @@ -51,38 +49,31 @@ 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). @@ -90,6 +81,7 @@ init_per_testcase(Testcase, Config) -> end_per_testcase(Testcase, Config) -> rabbit_ct_helpers:testcase_finished(Config, Testcase). + %% ------------------------------------------------------------------- %% Testsuite cases %% ------------------------------------------------------------------- @@ -112,7 +104,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). @@ -124,7 +116,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). @@ -136,19 +128,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 32d46f0481..71183d5f9c 100644 --- a/deps/rabbitmq_mqtt/test/shared_SUITE.erl +++ b/deps/rabbitmq_mqtt/test/shared_SUITE.erl @@ -6,10 +6,8 @@ %% 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"). @@ -17,113 +15,100 @@ -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 - ]}, - {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_1, [], + [ + {global_counters, [], + [ + global_counters_v3, + global_counters_v4 ]}, - {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 + {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 + ]} ]. suite() -> @@ -144,67 +129,54 @@ 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() - ). + 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 --> +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) -> @@ -215,10 +187,9 @@ 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) -> @@ -248,14 +219,11 @@ 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). @@ -267,14 +235,11 @@ 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). @@ -282,32 +247,26 @@ 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), @@ -326,11 +285,8 @@ 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), @@ -340,21 +296,14 @@ 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), @@ -390,55 +339,40 @@ 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) - ), + 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 - ), + 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">>, @@ -452,9 +386,8 @@ 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)), @@ -465,47 +398,27 @@ flow(Config, {App, Par, Val}, QueueType) when 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. @@ -519,27 +432,20 @@ 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), @@ -547,50 +453,37 @@ 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">>), @@ -618,9 +511,7 @@ 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). @@ -648,82 +539,62 @@ 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), @@ -740,14 +611,9 @@ 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), @@ -761,16 +627,9 @@ 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}]), @@ -794,16 +653,12 @@ 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. @@ -820,10 +675,8 @@ 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), @@ -831,24 +684,17 @@ 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) -> @@ -859,13 +705,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 @@ -873,26 +719,15 @@ 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. @@ -913,23 +748,12 @@ 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). @@ -942,15 +766,13 @@ 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]). @@ -967,36 +789,28 @@ 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 @@ -1004,12 +818,8 @@ 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), @@ -1027,25 +837,19 @@ 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">>]), @@ -1059,10 +863,8 @@ 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), @@ -1071,22 +873,17 @@ 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), @@ -1098,14 +895,9 @@ 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, [<<"/">>]), @@ -1118,13 +910,9 @@ 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) -> @@ -1146,14 +934,10 @@ large_message_amqp_to_mqtt(Config) -> Ch = rabbit_ct_client_helpers:open_channel(Config), Payload0 = binary:copy(<<"x">>, 8_000_000), Payload = <>, - 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). @@ -1163,14 +947,10 @@ 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). @@ -1182,12 +962,9 @@ 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). @@ -1218,21 +995,15 @@ 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). @@ -1257,54 +1028,25 @@ 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 - ), + 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"] - ) - ), + ?assertEqual([], + rabbit_ct_broker_helpers:rabbitmqctl_list( + Config, 1, ["list_queues", "--local", "--no-table-headers"]) + ), ok = emqtt:disconnect(C). @@ -1336,39 +1078,30 @@ 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]), @@ -1376,16 +1109,12 @@ 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). @@ -1400,7 +1129,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))), @@ -1438,11 +1167,9 @@ 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) -> @@ -1510,9 +1237,7 @@ 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. - - %% Give some time to clean up exclusive classic queue. - timer:sleep(200), + timer:sleep(200), %% Give some time to clean up exclusive classic queue. L = rpc(Config, rabbit_amqqueue, list, []), ?assertEqual(0, length(L)). @@ -1555,12 +1280,9 @@ 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. @@ -1572,65 +1294,45 @@ 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 = <<"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 = <<"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]]. @@ -1649,7 +1351,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) -> @@ -1661,45 +1363,32 @@ 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 - } - ). + Ch, #'queue.declare'{ + queue = QueueName, + durable = true, + arguments = Args}). -delete_queue(Ch, QueueNames) when - is_pid(Ch), is_list(QueueNames) --> +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 - } - ). + Ch, #'queue.delete'{ + queue = QueueName}). -bind(Ch, QueueName, Topic) when - is_pid(Ch), is_binary(QueueName), is_binary(Topic) --> +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 38689b2110..44c684a6c5 100644 --- a/deps/rabbitmq_mqtt/test/util.erl +++ b/deps/rabbitmq_mqtt/test/util.erl @@ -4,58 +4,46 @@ -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) -> @@ -63,27 +51,20 @@ 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) -> @@ -97,14 +78,11 @@ 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) -> - %% events are sent and processed asynchronously - timer:sleep(300), + timer:sleep(300), %% events are sent and processed asynchronously Result = gen_event:call({rabbit_event, Node}, event_recorder, take_state), ?assert(is_list(Result)), Result. @@ -114,26 +92,24 @@ 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) -> @@ -144,27 +120,21 @@ 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 66b6f15c14..3d058500ab 100644 --- a/deps/rabbitmq_mqtt/test/util_SUITE.erl +++ b/deps/rabbitmq_mqtt/test/util_SUITE.erl @@ -12,18 +12,19 @@ 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 7dafc013f2..7070128205 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,8 +58,7 @@ 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. @@ -71,38 +70,36 @@ 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; @@ -110,28 +107,25 @@ 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; @@ -139,45 +133,34 @@ 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) -> @@ -194,7 +177,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 2f017af696..72f52b3977 100644 --- a/deps/rabbitmq_web_mqtt/src/rabbit_web_mqtt_handler.erl +++ b/deps/rabbitmq_web_mqtt/src/rabbit_web_mqtt_handler.erl @@ -24,25 +24,23 @@ -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{}. @@ -60,22 +58,14 @@ 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) -> @@ -85,20 +75,22 @@ 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(), @@ -108,15 +100,12 @@ 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}; @@ -124,28 +113,24 @@ 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) -> @@ -153,8 +138,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) -> @@ -164,66 +149,39 @@ 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}; @@ -231,24 +189,18 @@ websocket_info( ?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}); @@ -275,16 +227,10 @@ 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), @@ -306,49 +252,29 @@ 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} -> @@ -363,11 +289,9 @@ 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. @@ -384,34 +308,26 @@ 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. @@ -425,23 +341,18 @@ 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), @@ -453,13 +364,12 @@ 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; @@ -473,23 +383,22 @@ 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 b407623da6..68d6ad2f29 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,6 +14,7 @@ -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 2106d0a4ed..ec256f4421 100644 --- a/deps/rabbitmq_web_mqtt/test/config_schema_SUITE.erl +++ b/deps/rabbitmq_web_mqtt/test/config_schema_SUITE.erl @@ -23,6 +23,7 @@ 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). @@ -30,19 +31,15 @@ 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). %% ------------------------------------------------------------------- @@ -50,13 +47,9 @@ 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 c6512cd6b6..95cd3e5c44 100644 --- a/deps/rabbitmq_web_mqtt/test/proxy_protocol_SUITE.erl +++ b/deps/rabbitmq_web_mqtt/test/proxy_protocol_SUITE.erl @@ -7,6 +7,7 @@ -module(proxy_protocol_SUITE). + -compile([export_all, nowarn_export_all]). -include_lib("common_test/include/ct.hrl"). @@ -14,24 +15,20 @@ 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(), @@ -41,29 +38,22 @@ 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), @@ -74,16 +64,12 @@ 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). @@ -95,23 +81,13 @@ 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 6642ae2d5a..19b0f873c4 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,19 +35,15 @@ 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. @@ -76,9 +72,7 @@ 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)). @@ -116,8 +110,7 @@ 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 e291e7625a..0590027d96 100644 --- a/rebar.config +++ b/rebar.config @@ -14,10 +14,3 @@ inline_items => {when_under, 4} }} ]}. - -{project_plugins, [erlfmt]}. -{erlfmt, [ - write, - {print_width, 100}, - {files, "deps/{rabbitmq_mqtt,rabbitmq_web_mqtt}/{test,src}/*.erl"} -]}.