summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/app_utils.erl127
-rw-r--r--src/credit_flow.erl220
-rw-r--r--src/gen_server2.erl1364
-rw-r--r--src/mirrored_supervisor.erl517
-rw-r--r--src/mochijson2.erl893
-rw-r--r--src/pmon.erl109
-rw-r--r--src/priority_queue.erl227
-rw-r--r--src/rabbit_amqqueue.erl908
-rw-r--r--src/rabbit_auth_mechanism.erl56
-rw-r--r--src/rabbit_authn_backend.erl49
-rw-r--r--src/rabbit_authz_backend.erl76
-rw-r--r--src/rabbit_backing_queue.erl282
-rw-r--r--src/rabbit_basic.erl326
-rw-r--r--src/rabbit_binary_generator.erl241
-rw-r--r--src/rabbit_binary_parser.erl161
-rw-r--r--src/rabbit_channel.erl2017
-rw-r--r--src/rabbit_channel_interceptor.erl117
-rw-r--r--src/rabbit_command_assembler.erl137
-rw-r--r--src/rabbit_event.erl163
-rw-r--r--src/rabbit_exchange_decorator.erl128
-rw-r--r--src/rabbit_exchange_type.erl81
-rw-r--r--src/rabbit_heartbeat.erl166
-rw-r--r--src/rabbit_misc.erl1165
-rw-r--r--src/rabbit_msg_store_index.erl59
-rw-r--r--src/rabbit_net.erl246
-rw-r--r--src/rabbit_networking.erl608
-rw-r--r--src/rabbit_nodes.erl221
-rw-r--r--src/rabbit_policy_validator.erl39
-rw-r--r--src/rabbit_queue_collector.erl95
-rw-r--r--src/rabbit_queue_decorator.erl80
-rw-r--r--src/rabbit_reader.erl1412
-rw-r--r--src/rabbit_runtime_parameter.erl42
-rw-r--r--src/rabbit_writer.erl390
-rw-r--r--src/ssl_compat.erl75
-rw-r--r--src/supervisor2.erl1553
-rw-r--r--src/time_compat.erl305
36 files changed, 0 insertions, 14655 deletions
diff --git a/src/app_utils.erl b/src/app_utils.erl
deleted file mode 100644
index bab327eab6..0000000000
--- a/src/app_utils.erl
+++ /dev/null
@@ -1,127 +0,0 @@
-%% The contents of this file are subject to the Mozilla Public License
-%% Version 1.1 (the "License"); you may not use this file except in
-%% compliance with the License. You may obtain a copy of the License
-%% at http://www.mozilla.org/MPL/
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and
-%% limitations under the License.
-%%
-%% The Original Code is RabbitMQ.
-%%
-%% The Initial Developer of the Original Code is GoPivotal, Inc.
-%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved.
-%%
--module(app_utils).
-
--export([load_applications/1, start_applications/1, start_applications/2,
- stop_applications/1, stop_applications/2, app_dependency_order/2,
- app_dependencies/1]).
-
--ifdef(use_specs).
-
--type error_handler() :: fun((atom(), any()) -> 'ok').
-
--spec load_applications([atom()]) -> 'ok'.
--spec start_applications([atom()]) -> 'ok'.
--spec stop_applications([atom()]) -> 'ok'.
--spec start_applications([atom()], error_handler()) -> 'ok'.
--spec stop_applications([atom()], error_handler()) -> 'ok'.
--spec app_dependency_order([atom()], boolean()) -> [digraph:vertex()].
--spec app_dependencies(atom()) -> [atom()].
-
--endif.
-
-%%---------------------------------------------------------------------------
-%% Public API
-
-load_applications(Apps) ->
- load_applications(queue:from_list(Apps), sets:new()),
- ok.
-
-start_applications(Apps) ->
- start_applications(
- Apps, fun (App, Reason) ->
- throw({error, {cannot_start_application, App, Reason}})
- end).
-
-stop_applications(Apps) ->
- stop_applications(
- Apps, fun (App, Reason) ->
- throw({error, {cannot_stop_application, App, Reason}})
- end).
-
-start_applications(Apps, ErrorHandler) ->
- manage_applications(fun lists:foldl/3,
- fun application:start/1,
- fun application:stop/1,
- already_started,
- ErrorHandler,
- Apps).
-
-stop_applications(Apps, ErrorHandler) ->
- manage_applications(fun lists:foldr/3,
- fun application:stop/1,
- fun application:start/1,
- not_started,
- ErrorHandler,
- Apps).
-
-app_dependency_order(RootApps, StripUnreachable) ->
- {ok, G} = rabbit_misc:build_acyclic_graph(
- fun ({App, _Deps}) -> [{App, App}] end,
- fun ({App, Deps}) -> [{Dep, App} || Dep <- Deps] end,
- [{App, app_dependencies(App)} ||
- {App, _Desc, _Vsn} <- application:loaded_applications()]),
- try
- case StripUnreachable of
- true -> digraph:del_vertices(G, digraph:vertices(G) --
- digraph_utils:reachable(RootApps, G));
- false -> ok
- end,
- digraph_utils:topsort(G)
- after
- true = digraph:delete(G)
- end.
-
-%%---------------------------------------------------------------------------
-%% Private API
-
-load_applications(Worklist, Loaded) ->
- case queue:out(Worklist) of
- {empty, _WorkList} ->
- ok;
- {{value, App}, Worklist1} ->
- case sets:is_element(App, Loaded) of
- true -> load_applications(Worklist1, Loaded);
- false -> case application:load(App) of
- ok -> ok;
- {error, {already_loaded, App}} -> ok;
- Error -> throw(Error)
- end,
- load_applications(
- queue:join(Worklist1,
- queue:from_list(app_dependencies(App))),
- sets:add_element(App, Loaded))
- end
- end.
-
-app_dependencies(App) ->
- case application:get_key(App, applications) of
- undefined -> [];
- {ok, Lst} -> Lst
- end.
-
-manage_applications(Iterate, Do, Undo, SkipError, ErrorHandler, Apps) ->
- Iterate(fun (App, Acc) ->
- case Do(App) of
- ok -> [App | Acc];
- {error, {SkipError, _}} -> Acc;
- {error, Reason} ->
- lists:foreach(Undo, Acc),
- ErrorHandler(App, Reason)
- end
- end, [], Apps),
- ok.
-
diff --git a/src/credit_flow.erl b/src/credit_flow.erl
deleted file mode 100644
index 8c8d340601..0000000000
--- a/src/credit_flow.erl
+++ /dev/null
@@ -1,220 +0,0 @@
-%% The contents of this file are subject to the Mozilla Public License
-%% Version 1.1 (the "License"); you may not use this file except in
-%% compliance with the License. You may obtain a copy of the License
-%% at http://www.mozilla.org/MPL/
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and
-%% limitations under the License.
-%%
-%% The Original Code is RabbitMQ.
-%%
-%% The Initial Developer of the Original Code is GoPivotal, Inc.
-%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved.
-%%
-
--module(credit_flow).
-
-%% Credit flow is controlled by a credit specification - a
-%% {InitialCredit, MoreCreditAfter} tuple. For the message sender,
-%% credit starts at InitialCredit and is decremented with every
-%% message sent. The message receiver grants more credit to the sender
-%% by sending it a {bump_credit, ...} control message after receiving
-%% MoreCreditAfter messages. The sender should pass this message in to
-%% handle_bump_msg/1. The sender should block when it goes below 0
-%% (check by invoking blocked/0). If a process is both a sender and a
-%% receiver it will not grant any more credit to its senders when it
-%% is itself blocked - thus the only processes that need to check
-%% blocked/0 are ones that read from network sockets.
-%%
-%% Credit flows left to right when process send messags down the
-%% chain, starting at the rabbit_reader, ending at the msg_store:
-%% reader -> channel -> queue_process -> msg_store.
-%%
-%% If the message store has a back log, then it will block the
-%% queue_process, which will block the channel, and finally the reader
-%% will be blocked, throttling down publishers.
-%%
-%% Once a process is unblocked, it will grant credits up the chain,
-%% possibly unblocking other processes:
-%% reader <--grant channel <--grant queue_process <--grant msg_store.
-%%
-%% Grepping the project files for `credit_flow` will reveal the places
-%% where this module is currently used, with extra comments on what's
-%% going on at each instance. Note that credit flow between mirrors
-%% synchronization has not been documented, since this doesn't affect
-%% client publishes.
-
--define(DEFAULT_INITIAL_CREDIT, 200).
--define(DEFAULT_MORE_CREDIT_AFTER, 50).
-
--define(DEFAULT_CREDIT,
- case get(credit_flow_default_credit) of
- undefined ->
- Val = rabbit_misc:get_env(rabbit, credit_flow_default_credit,
- {?DEFAULT_INITIAL_CREDIT,
- ?DEFAULT_MORE_CREDIT_AFTER}),
- put(credit_flow_default_credit, Val),
- Val;
- Val -> Val
- end).
-
--export([send/1, send/2, ack/1, ack/2, handle_bump_msg/1, blocked/0, state/0]).
--export([peer_down/1]).
-
-%%----------------------------------------------------------------------------
-
--ifdef(use_specs).
-
--export_type([bump_msg/0]).
-
--opaque(bump_msg() :: {pid(), non_neg_integer()}).
--type(credit_spec() :: {non_neg_integer(), non_neg_integer()}).
-
--spec(send/1 :: (pid()) -> 'ok').
--spec(send/2 :: (pid(), credit_spec()) -> 'ok').
--spec(ack/1 :: (pid()) -> 'ok').
--spec(ack/2 :: (pid(), credit_spec()) -> 'ok').
--spec(handle_bump_msg/1 :: (bump_msg()) -> 'ok').
--spec(blocked/0 :: () -> boolean()).
--spec(peer_down/1 :: (pid()) -> 'ok').
-
--endif.
-
-%%----------------------------------------------------------------------------
-
-%% process dict update macro - eliminates the performance-hurting
-%% closure creation a HOF would introduce
--define(UPDATE(Key, Default, Var, Expr),
- begin
- %% We deliberately allow Var to escape from the case here
- %% to be used in Expr. Any temporary var we introduced
- %% would also escape, and might conflict.
- Var = case get(Key) of
- undefined -> Default;
- V -> V
- end,
- put(Key, Expr)
- end).
-
-%% If current process was blocked by credit flow in the last
-%% STATE_CHANGE_INTERVAL milliseconds, state/0 will report it as "in
-%% flow".
--define(STATE_CHANGE_INTERVAL, 1000000).
-
--ifdef(CREDIT_FLOW_TRACING).
--define(TRACE_BLOCKED(SELF, FROM), rabbit_event:notify(credit_flow_blocked,
- [{process, SELF},
- {process_info, erlang:process_info(SELF)},
- {from, FROM},
- {from_info, erlang:process_info(FROM)},
- {timestamp,
- time_compat:os_system_time(
- milliseconds)}])).
--define(TRACE_UNBLOCKED(SELF, FROM), rabbit_event:notify(credit_flow_unblocked,
- [{process, SELF},
- {from, FROM},
- {timestamp,
- time_compat:os_system_time(
- milliseconds)}])).
--else.
--define(TRACE_BLOCKED(SELF, FROM), ok).
--define(TRACE_UNBLOCKED(SELF, FROM), ok).
--endif.
-
-%%----------------------------------------------------------------------------
-
-%% There are two "flows" here; of messages and of credit, going in
-%% opposite directions. The variable names "From" and "To" refer to
-%% the flow of credit, but the function names refer to the flow of
-%% messages. This is the clearest I can make it (since the function
-%% names form the API and want to make sense externally, while the
-%% variable names are used in credit bookkeeping and want to make
-%% sense internally).
-
-%% For any given pair of processes, ack/2 and send/2 must always be
-%% called with the same credit_spec().
-
-send(From) -> send(From, ?DEFAULT_CREDIT).
-
-send(From, {InitialCredit, _MoreCreditAfter}) ->
- ?UPDATE({credit_from, From}, InitialCredit, C,
- if C == 1 -> block(From),
- 0;
- true -> C - 1
- end).
-
-ack(To) -> ack(To, ?DEFAULT_CREDIT).
-
-ack(To, {_InitialCredit, MoreCreditAfter}) ->
- ?UPDATE({credit_to, To}, MoreCreditAfter, C,
- if C == 1 -> grant(To, MoreCreditAfter),
- MoreCreditAfter;
- true -> C - 1
- end).
-
-handle_bump_msg({From, MoreCredit}) ->
- ?UPDATE({credit_from, From}, 0, C,
- if C =< 0 andalso C + MoreCredit > 0 -> unblock(From),
- C + MoreCredit;
- true -> C + MoreCredit
- end).
-
-blocked() -> case get(credit_blocked) of
- undefined -> false;
- [] -> false;
- _ -> true
- end.
-
-state() -> case blocked() of
- true -> flow;
- false -> case get(credit_blocked_at) of
- undefined -> running;
- B -> Now = time_compat:monotonic_time(),
- Diff = time_compat:convert_time_unit(Now - B,
- native,
- micro_seconds),
- case Diff < ?STATE_CHANGE_INTERVAL of
- true -> flow;
- false -> running
- end
- end
- end.
-
-peer_down(Peer) ->
- %% In theory we could also remove it from credit_deferred here, but it
- %% doesn't really matter; at some point later we will drain
- %% credit_deferred and thus send messages into the void...
- unblock(Peer),
- erase({credit_from, Peer}),
- erase({credit_to, Peer}),
- ok.
-
-%% --------------------------------------------------------------------------
-
-grant(To, Quantity) ->
- Msg = {bump_credit, {self(), Quantity}},
- case blocked() of
- false -> To ! Msg;
- true -> ?UPDATE(credit_deferred, [], Deferred, [{To, Msg} | Deferred])
- end.
-
-block(From) ->
- ?TRACE_BLOCKED(self(), From),
- case blocked() of
- false -> put(credit_blocked_at, time_compat:monotonic_time());
- true -> ok
- end,
- ?UPDATE(credit_blocked, [], Blocks, [From | Blocks]).
-
-unblock(From) ->
- ?TRACE_UNBLOCKED(self(), From),
- ?UPDATE(credit_blocked, [], Blocks, Blocks -- [From]),
- case blocked() of
- false -> case erase(credit_deferred) of
- undefined -> ok;
- Credits -> [To ! Msg || {To, Msg} <- Credits]
- end;
- true -> ok
- end.
diff --git a/src/gen_server2.erl b/src/gen_server2.erl
deleted file mode 100644
index 23494399d2..0000000000
--- a/src/gen_server2.erl
+++ /dev/null
@@ -1,1364 +0,0 @@
-%% This file is a copy of gen_server.erl from the R13B-1 Erlang/OTP
-%% distribution, with the following modifications:
-%%
-%% 1) the module name is gen_server2
-%%
-%% 2) more efficient handling of selective receives in callbacks
-%% gen_server2 processes drain their message queue into an internal
-%% buffer before invoking any callback module functions. Messages are
-%% dequeued from the buffer for processing. Thus the effective message
-%% queue of a gen_server2 process is the concatenation of the internal
-%% buffer and the real message queue.
-%% As a result of the draining, any selective receive invoked inside a
-%% callback is less likely to have to scan a large message queue.
-%%
-%% 3) gen_server2:cast is guaranteed to be order-preserving
-%% The original code could reorder messages when communicating with a
-%% process on a remote node that was not currently connected.
-%%
-%% 4) The callback module can optionally implement prioritise_call/4,
-%% prioritise_cast/3 and prioritise_info/3. These functions take
-%% Message, From, Length and State or just Message, Length and State
-%% (where Length is the current number of messages waiting to be
-%% processed) and return a single integer representing the priority
-%% attached to the message, or 'drop' to ignore it (for
-%% prioritise_cast/3 and prioritise_info/3 only). Messages with
-%% higher priorities are processed before requests with lower
-%% priorities. The default priority is 0.
-%%
-%% 5) The callback module can optionally implement
-%% handle_pre_hibernate/1 and handle_post_hibernate/1. These will be
-%% called immediately prior to and post hibernation, respectively. If
-%% handle_pre_hibernate returns {hibernate, NewState} then the process
-%% will hibernate. If the module does not implement
-%% handle_pre_hibernate/1 then the default action is to hibernate.
-%%
-%% 6) init can return a 4th arg, {backoff, InitialTimeout,
-%% MinimumTimeout, DesiredHibernatePeriod} (all in milliseconds,
-%% 'infinity' does not make sense here). Then, on all callbacks which
-%% can return a timeout (including init), timeout can be
-%% 'hibernate'. When this is the case, the current timeout value will
-%% be used (initially, the InitialTimeout supplied from init). After
-%% this timeout has occurred, hibernation will occur as normal. Upon
-%% awaking, a new current timeout value will be calculated.
-%%
-%% The purpose is that the gen_server2 takes care of adjusting the
-%% current timeout value such that the process will increase the
-%% timeout value repeatedly if it is unable to sleep for the
-%% DesiredHibernatePeriod. If it is able to sleep for the
-%% DesiredHibernatePeriod it will decrease the current timeout down to
-%% the MinimumTimeout, so that the process is put to sleep sooner (and
-%% hopefully stays asleep for longer). In short, should a process
-%% using this receive a burst of messages, it should not hibernate
-%% between those messages, but as the messages become less frequent,
-%% the process will not only hibernate, it will do so sooner after
-%% each message.
-%%
-%% When using this backoff mechanism, normal timeout values (i.e. not
-%% 'hibernate') can still be used, and if they are used then the
-%% handle_info(timeout, State) will be called as normal. In this case,
-%% returning 'hibernate' from handle_info(timeout, State) will not
-%% hibernate the process immediately, as it would if backoff wasn't
-%% being used. Instead it'll wait for the current timeout as described
-%% above.
-%%
-%% 7) The callback module can return from any of the handle_*
-%% functions, a {become, Module, State} triple, or a {become, Module,
-%% State, Timeout} quadruple. This allows the gen_server to
-%% dynamically change the callback module. The State is the new state
-%% which will be passed into any of the callback functions in the new
-%% module. Note there is no form also encompassing a reply, thus if
-%% you wish to reply in handle_call/3 and change the callback module,
-%% you need to use gen_server2:reply/2 to issue the reply
-%% manually. The init function can similarly return a 5th argument,
-%% Module, in order to dynamically decide the callback module on init.
-%%
-%% 8) The callback module can optionally implement
-%% format_message_queue/2 which is the equivalent of format_status/2
-%% but where the second argument is specifically the priority_queue
-%% which contains the prioritised message_queue.
-%%
-%% 9) The function with_state/2 can be used to debug a process with
-%% heavyweight state (without needing to copy the entire state out of
-%% process as sys:get_status/1 would). Pass through a function which
-%% can be invoked on the state, get back the result. The state is not
-%% modified.
-%%
-%% 10) an mcall/1 function has been added for performing multiple
-%% call/3 in parallel. Unlike multi_call, which sends the same request
-%% to same-named processes residing on a supplied list of nodes, it
-%% operates on name/request pairs, where name is anything accepted by
-%% call/3, i.e. a pid, global name, local name, or local name on a
-%% particular node.
-%%
-
-%% All modifications are (C) 2009-2013 GoPivotal, Inc.
-
-%% ``The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved via the world wide web at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
-%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
-%% AB. All Rights Reserved.''
-%%
-%% $Id$
-%%
--module(gen_server2).
-
-%%% ---------------------------------------------------
-%%%
-%%% The idea behind THIS server is that the user module
-%%% provides (different) functions to handle different
-%%% kind of inputs.
-%%% If the Parent process terminates the Module:terminate/2
-%%% function is called.
-%%%
-%%% The user module should export:
-%%%
-%%% init(Args)
-%%% ==> {ok, State}
-%%% {ok, State, Timeout}
-%%% {ok, State, Timeout, Backoff}
-%%% {ok, State, Timeout, Backoff, Module}
-%%% ignore
-%%% {stop, Reason}
-%%%
-%%% handle_call(Msg, {From, Tag}, State)
-%%%
-%%% ==> {reply, Reply, State}
-%%% {reply, Reply, State, Timeout}
-%%% {noreply, State}
-%%% {noreply, State, Timeout}
-%%% {stop, Reason, Reply, State}
-%%% Reason = normal | shutdown | Term terminate(State) is called
-%%%
-%%% handle_cast(Msg, State)
-%%%
-%%% ==> {noreply, State}
-%%% {noreply, State, Timeout}
-%%% {stop, Reason, State}
-%%% Reason = normal | shutdown | Term terminate(State) is called
-%%%
-%%% handle_info(Info, State) Info is e.g. {'EXIT', P, R}, {nodedown, N}, ...
-%%%
-%%% ==> {noreply, State}
-%%% {noreply, State, Timeout}
-%%% {stop, Reason, State}
-%%% Reason = normal | shutdown | Term, terminate(State) is called
-%%%
-%%% terminate(Reason, State) Let the user module clean up
-%%% Reason = normal | shutdown | {shutdown, Term} | Term
-%%% always called when server terminates
-%%%
-%%% ==> ok | Term
-%%%
-%%% handle_pre_hibernate(State)
-%%%
-%%% ==> {hibernate, State}
-%%% {stop, Reason, State}
-%%% Reason = normal | shutdown | Term, terminate(State) is called
-%%%
-%%% handle_post_hibernate(State)
-%%%
-%%% ==> {noreply, State}
-%%% {stop, Reason, State}
-%%% Reason = normal | shutdown | Term, terminate(State) is called
-%%%
-%%% The work flow (of the server) can be described as follows:
-%%%
-%%% User module Generic
-%%% ----------- -------
-%%% start -----> start
-%%% init <----- .
-%%%
-%%% loop
-%%% handle_call <----- .
-%%% -----> reply
-%%%
-%%% handle_cast <----- .
-%%%
-%%% handle_info <----- .
-%%%
-%%% terminate <----- .
-%%%
-%%% -----> reply
-%%%
-%%%
-%%% ---------------------------------------------------
-
-%% API
--export([start/3, start/4,
- start_link/3, start_link/4,
- call/2, call/3,
- cast/2, reply/2,
- abcast/2, abcast/3,
- multi_call/2, multi_call/3, multi_call/4,
- mcall/1,
- with_state/2,
- enter_loop/3, enter_loop/4, enter_loop/5, enter_loop/6, wake_hib/1]).
-
-%% System exports
--export([system_continue/3,
- system_terminate/4,
- system_code_change/4,
- format_status/2]).
-
-%% Internal exports
--export([init_it/6]).
-
--import(error_logger, [format/2]).
-
-%% State record
--record(gs2_state, {parent, name, state, mod, time,
- timeout_state, queue, debug, prioritisers}).
-
--ifdef(use_specs).
-
-%%%=========================================================================
-%%% Specs. These exist only to shut up dialyzer's warnings
-%%%=========================================================================
-
--type(gs2_state() :: #gs2_state{}).
-
--spec(handle_common_termination/3 ::
- (any(), atom(), gs2_state()) -> no_return()).
--spec(hibernate/1 :: (gs2_state()) -> no_return()).
--spec(pre_hibernate/1 :: (gs2_state()) -> no_return()).
--spec(system_terminate/4 :: (_, _, _, gs2_state()) -> no_return()).
-
--type(millis() :: non_neg_integer()).
-
-%%%=========================================================================
-%%% API
-%%%=========================================================================
-
--callback init(Args :: term()) ->
- {ok, State :: term()} |
- {ok, State :: term(), timeout() | hibernate} |
- {ok, State :: term(), timeout() | hibernate,
- {backoff, millis(), millis(), millis()}} |
- {ok, State :: term(), timeout() | hibernate,
- {backoff, millis(), millis(), millis()}, atom()} |
- ignore |
- {stop, Reason :: term()}.
--callback handle_call(Request :: term(), From :: {pid(), Tag :: term()},
- State :: term()) ->
- {reply, Reply :: term(), NewState :: term()} |
- {reply, Reply :: term(), NewState :: term(), timeout() | hibernate} |
- {noreply, NewState :: term()} |
- {noreply, NewState :: term(), timeout() | hibernate} |
- {stop, Reason :: term(),
- Reply :: term(), NewState :: term()}.
--callback handle_cast(Request :: term(), State :: term()) ->
- {noreply, NewState :: term()} |
- {noreply, NewState :: term(), timeout() | hibernate} |
- {stop, Reason :: term(), NewState :: term()}.
--callback handle_info(Info :: term(), State :: term()) ->
- {noreply, NewState :: term()} |
- {noreply, NewState :: term(), timeout() | hibernate} |
- {stop, Reason :: term(), NewState :: term()}.
--callback terminate(Reason :: (normal | shutdown | {shutdown, term()} | term()),
- State :: term()) ->
- ok | term().
--callback code_change(OldVsn :: (term() | {down, term()}), State :: term(),
- Extra :: term()) ->
- {ok, NewState :: term()} | {error, Reason :: term()}.
-
-%% It's not possible to define "optional" -callbacks, so putting specs
-%% for handle_pre_hibernate/1 and handle_post_hibernate/1 will result
-%% in warnings (the same applied for the behaviour_info before).
-
--else.
-
--export([behaviour_info/1]).
-
-behaviour_info(callbacks) ->
- [{init,1},{handle_call,3},{handle_cast,2},{handle_info,2},
- {terminate,2},{code_change,3}];
-behaviour_info(_Other) ->
- undefined.
-
--endif.
-
-%%% -----------------------------------------------------------------
-%%% Starts a generic server.
-%%% start(Mod, Args, Options)
-%%% start(Name, Mod, Args, Options)
-%%% start_link(Mod, Args, Options)
-%%% start_link(Name, Mod, Args, Options) where:
-%%% Name ::= {local, atom()} | {global, atom()}
-%%% Mod ::= atom(), callback module implementing the 'real' server
-%%% Args ::= term(), init arguments (to Mod:init/1)
-%%% Options ::= [{timeout, Timeout} | {debug, [Flag]}]
-%%% Flag ::= trace | log | {logfile, File} | statistics | debug
-%%% (debug == log && statistics)
-%%% Returns: {ok, Pid} |
-%%% {error, {already_started, Pid}} |
-%%% {error, Reason}
-%%% -----------------------------------------------------------------
-start(Mod, Args, Options) ->
- gen:start(?MODULE, nolink, Mod, Args, Options).
-
-start(Name, Mod, Args, Options) ->
- gen:start(?MODULE, nolink, Name, Mod, Args, Options).
-
-start_link(Mod, Args, Options) ->
- gen:start(?MODULE, link, Mod, Args, Options).
-
-start_link(Name, Mod, Args, Options) ->
- gen:start(?MODULE, link, Name, Mod, Args, Options).
-
-
-%% -----------------------------------------------------------------
-%% Make a call to a generic server.
-%% If the server is located at another node, that node will
-%% be monitored.
-%% If the client is trapping exits and is linked server termination
-%% is handled here (? Shall we do that here (or rely on timeouts) ?).
-%% -----------------------------------------------------------------
-call(Name, Request) ->
- case catch gen:call(Name, '$gen_call', Request) of
- {ok,Res} ->
- Res;
- {'EXIT',Reason} ->
- exit({Reason, {?MODULE, call, [Name, Request]}})
- end.
-
-call(Name, Request, Timeout) ->
- case catch gen:call(Name, '$gen_call', Request, Timeout) of
- {ok,Res} ->
- Res;
- {'EXIT',Reason} ->
- exit({Reason, {?MODULE, call, [Name, Request, Timeout]}})
- end.
-
-%% -----------------------------------------------------------------
-%% Make a cast to a generic server.
-%% -----------------------------------------------------------------
-cast({global,Name}, Request) ->
- catch global:send(Name, cast_msg(Request)),
- ok;
-cast({Name,Node}=Dest, Request) when is_atom(Name), is_atom(Node) ->
- do_cast(Dest, Request);
-cast(Dest, Request) when is_atom(Dest) ->
- do_cast(Dest, Request);
-cast(Dest, Request) when is_pid(Dest) ->
- do_cast(Dest, Request).
-
-do_cast(Dest, Request) ->
- do_send(Dest, cast_msg(Request)),
- ok.
-
-cast_msg(Request) -> {'$gen_cast',Request}.
-
-%% -----------------------------------------------------------------
-%% Send a reply to the client.
-%% -----------------------------------------------------------------
-reply({To, Tag}, Reply) ->
- catch To ! {Tag, Reply}.
-
-%% -----------------------------------------------------------------
-%% Asyncronous broadcast, returns nothing, it's just send'n pray
-%% -----------------------------------------------------------------
-abcast(Name, Request) when is_atom(Name) ->
- do_abcast([node() | nodes()], Name, cast_msg(Request)).
-
-abcast(Nodes, Name, Request) when is_list(Nodes), is_atom(Name) ->
- do_abcast(Nodes, Name, cast_msg(Request)).
-
-do_abcast([Node|Nodes], Name, Msg) when is_atom(Node) ->
- do_send({Name,Node},Msg),
- do_abcast(Nodes, Name, Msg);
-do_abcast([], _,_) -> abcast.
-
-%%% -----------------------------------------------------------------
-%%% Make a call to servers at several nodes.
-%%% Returns: {[Replies],[BadNodes]}
-%%% A Timeout can be given
-%%%
-%%% A middleman process is used in case late answers arrives after
-%%% the timeout. If they would be allowed to glog the callers message
-%%% queue, it would probably become confused. Late answers will
-%%% now arrive to the terminated middleman and so be discarded.
-%%% -----------------------------------------------------------------
-multi_call(Name, Req)
- when is_atom(Name) ->
- do_multi_call([node() | nodes()], Name, Req, infinity).
-
-multi_call(Nodes, Name, Req)
- when is_list(Nodes), is_atom(Name) ->
- do_multi_call(Nodes, Name, Req, infinity).
-
-multi_call(Nodes, Name, Req, infinity) ->
- do_multi_call(Nodes, Name, Req, infinity);
-multi_call(Nodes, Name, Req, Timeout)
- when is_list(Nodes), is_atom(Name), is_integer(Timeout), Timeout >= 0 ->
- do_multi_call(Nodes, Name, Req, Timeout).
-
-%%% -----------------------------------------------------------------
-%%% Make multiple calls to multiple servers, given pairs of servers
-%%% and messages.
-%%% Returns: {[{Dest, Reply}], [{Dest, Error}]}
-%%%
-%%% Dest can be pid() | RegName :: atom() |
-%%% {Name :: atom(), Node :: atom()} | {global, Name :: atom()}
-%%%
-%%% A middleman process is used to avoid clogging up the callers
-%%% message queue.
-%%% -----------------------------------------------------------------
-mcall(CallSpecs) ->
- Tag = make_ref(),
- {_, MRef} = spawn_monitor(
- fun() ->
- Refs = lists:foldl(
- fun ({Dest, _Request}=S, Dict) ->
- dict:store(do_mcall(S), Dest, Dict)
- end, dict:new(), CallSpecs),
- collect_replies(Tag, Refs, [], [])
- end),
- receive
- {'DOWN', MRef, _, _, {Tag, Result}} -> Result;
- {'DOWN', MRef, _, _, Reason} -> exit(Reason)
- end.
-
-do_mcall({{global,Name}=Dest, Request}) ->
- %% whereis_name is simply an ets lookup, and is precisely what
- %% global:send/2 does, yet we need a Ref to put in the call to the
- %% server, so invoking whereis_name makes a lot more sense here.
- case global:whereis_name(Name) of
- Pid when is_pid(Pid) ->
- MRef = erlang:monitor(process, Pid),
- catch msend(Pid, MRef, Request),
- MRef;
- undefined ->
- Ref = make_ref(),
- self() ! {'DOWN', Ref, process, Dest, noproc},
- Ref
- end;
-do_mcall({{Name,Node}=Dest, Request}) when is_atom(Name), is_atom(Node) ->
- {_Node, MRef} = start_monitor(Node, Name), %% NB: we don't handle R6
- catch msend(Dest, MRef, Request),
- MRef;
-do_mcall({Dest, Request}) when is_atom(Dest); is_pid(Dest) ->
- MRef = erlang:monitor(process, Dest),
- catch msend(Dest, MRef, Request),
- MRef.
-
-msend(Dest, MRef, Request) ->
- erlang:send(Dest, {'$gen_call', {self(), MRef}, Request}, [noconnect]).
-
-collect_replies(Tag, Refs, Replies, Errors) ->
- case dict:size(Refs) of
- 0 -> exit({Tag, {Replies, Errors}});
- _ -> receive
- {MRef, Reply} ->
- {Refs1, Replies1} = handle_call_result(MRef, Reply,
- Refs, Replies),
- collect_replies(Tag, Refs1, Replies1, Errors);
- {'DOWN', MRef, _, _, Reason} ->
- Reason1 = case Reason of
- noconnection -> nodedown;
- _ -> Reason
- end,
- {Refs1, Errors1} = handle_call_result(MRef, Reason1,
- Refs, Errors),
- collect_replies(Tag, Refs1, Replies, Errors1)
- end
- end.
-
-handle_call_result(MRef, Result, Refs, AccList) ->
- %% we avoid the mailbox scanning cost of a call to erlang:demonitor/{1,2}
- %% here, so we must cope with MRefs that we've already seen and erased
- case dict:find(MRef, Refs) of
- {ok, Pid} -> {dict:erase(MRef, Refs), [{Pid, Result}|AccList]};
- _ -> {Refs, AccList}
- end.
-
-%% -----------------------------------------------------------------
-%% Apply a function to a generic server's state.
-%% -----------------------------------------------------------------
-with_state(Name, Fun) ->
- case catch gen:call(Name, '$with_state', Fun, infinity) of
- {ok,Res} ->
- Res;
- {'EXIT',Reason} ->
- exit({Reason, {?MODULE, with_state, [Name, Fun]}})
- end.
-
-%%-----------------------------------------------------------------
-%% enter_loop(Mod, Options, State, <ServerName>, <TimeOut>, <Backoff>) ->_
-%%
-%% Description: Makes an existing process into a gen_server.
-%% The calling process will enter the gen_server receive
-%% loop and become a gen_server process.
-%% The process *must* have been started using one of the
-%% start functions in proc_lib, see proc_lib(3).
-%% The user is responsible for any initialization of the
-%% process, including registering a name for it.
-%%-----------------------------------------------------------------
-enter_loop(Mod, Options, State) ->
- enter_loop(Mod, Options, State, self(), infinity, undefined).
-
-enter_loop(Mod, Options, State, Backoff = {backoff, _, _ , _}) ->
- enter_loop(Mod, Options, State, self(), infinity, Backoff);
-
-enter_loop(Mod, Options, State, ServerName = {_, _}) ->
- enter_loop(Mod, Options, State, ServerName, infinity, undefined);
-
-enter_loop(Mod, Options, State, Timeout) ->
- enter_loop(Mod, Options, State, self(), Timeout, undefined).
-
-enter_loop(Mod, Options, State, ServerName, Backoff = {backoff, _, _, _}) ->
- enter_loop(Mod, Options, State, ServerName, infinity, Backoff);
-
-enter_loop(Mod, Options, State, ServerName, Timeout) ->
- enter_loop(Mod, Options, State, ServerName, Timeout, undefined).
-
-enter_loop(Mod, Options, State, ServerName, Timeout, Backoff) ->
- Name = get_proc_name(ServerName),
- Parent = get_parent(),
- Debug = debug_options(Name, Options),
- Queue = priority_queue:new(),
- Backoff1 = extend_backoff(Backoff),
- loop(find_prioritisers(
- #gs2_state { parent = Parent, name = Name, state = State,
- mod = Mod, time = Timeout, timeout_state = Backoff1,
- queue = Queue, debug = Debug })).
-
-%%%========================================================================
-%%% Gen-callback functions
-%%%========================================================================
-
-%%% ---------------------------------------------------
-%%% Initiate the new process.
-%%% Register the name using the Rfunc function
-%%% Calls the Mod:init/Args function.
-%%% Finally an acknowledge is sent to Parent and the main
-%%% loop is entered.
-%%% ---------------------------------------------------
-init_it(Starter, self, Name, Mod, Args, Options) ->
- init_it(Starter, self(), Name, Mod, Args, Options);
-init_it(Starter, Parent, Name0, Mod, Args, Options) ->
- Name = name(Name0),
- Debug = debug_options(Name, Options),
- Queue = priority_queue:new(),
- GS2State = find_prioritisers(
- #gs2_state { parent = Parent,
- name = Name,
- mod = Mod,
- queue = Queue,
- debug = Debug }),
- case catch Mod:init(Args) of
- {ok, State} ->
- proc_lib:init_ack(Starter, {ok, self()}),
- loop(GS2State #gs2_state { state = State,
- time = infinity,
- timeout_state = undefined });
- {ok, State, Timeout} ->
- proc_lib:init_ack(Starter, {ok, self()}),
- loop(GS2State #gs2_state { state = State,
- time = Timeout,
- timeout_state = undefined });
- {ok, State, Timeout, Backoff = {backoff, _, _, _}} ->
- Backoff1 = extend_backoff(Backoff),
- proc_lib:init_ack(Starter, {ok, self()}),
- loop(GS2State #gs2_state { state = State,
- time = Timeout,
- timeout_state = Backoff1 });
- {ok, State, Timeout, Backoff = {backoff, _, _, _}, Mod1} ->
- Backoff1 = extend_backoff(Backoff),
- proc_lib:init_ack(Starter, {ok, self()}),
- loop(find_prioritisers(
- GS2State #gs2_state { mod = Mod1,
- state = State,
- time = Timeout,
- timeout_state = Backoff1 }));
- {stop, Reason} ->
- %% For consistency, we must make sure that the
- %% registered name (if any) is unregistered before
- %% the parent process is notified about the failure.
- %% (Otherwise, the parent process could get
- %% an 'already_started' error if it immediately
- %% tried starting the process again.)
- unregister_name(Name0),
- proc_lib:init_ack(Starter, {error, Reason}),
- exit(Reason);
- ignore ->
- unregister_name(Name0),
- proc_lib:init_ack(Starter, ignore),
- exit(normal);
- {'EXIT', Reason} ->
- unregister_name(Name0),
- proc_lib:init_ack(Starter, {error, Reason}),
- exit(Reason);
- Else ->
- Error = {bad_return_value, Else},
- proc_lib:init_ack(Starter, {error, Error}),
- exit(Error)
- end.
-
-name({local,Name}) -> Name;
-name({global,Name}) -> Name;
-%% name(Pid) when is_pid(Pid) -> Pid;
-%% when R12 goes away, drop the line beneath and uncomment the line above
-name(Name) -> Name.
-
-unregister_name({local,Name}) ->
- _ = (catch unregister(Name));
-unregister_name({global,Name}) ->
- _ = global:unregister_name(Name);
-unregister_name(Pid) when is_pid(Pid) ->
- Pid;
-%% Under R12 let's just ignore it, as we have a single term as Name.
-%% On R13 it will never get here, as we get tuple with 'local/global' atom.
-unregister_name(_Name) -> ok.
-
-extend_backoff(undefined) ->
- undefined;
-extend_backoff({backoff, InitialTimeout, MinimumTimeout, DesiredHibPeriod}) ->
- {backoff, InitialTimeout, MinimumTimeout, DesiredHibPeriod,
- {erlang:phash2([node()]),
- time_compat:monotonic_time(),
- time_compat:unique_integer()}}.
-
-%%%========================================================================
-%%% Internal functions
-%%%========================================================================
-%%% ---------------------------------------------------
-%%% The MAIN loop.
-%%% ---------------------------------------------------
-loop(GS2State = #gs2_state { time = hibernate,
- timeout_state = undefined,
- queue = Queue }) ->
- case priority_queue:is_empty(Queue) of
- true ->
- pre_hibernate(GS2State);
- false ->
- process_next_msg(GS2State)
- end;
-
-loop(GS2State) ->
- process_next_msg(drain(GS2State)).
-
-drain(GS2State) ->
- receive
- Input -> drain(in(Input, GS2State))
- after 0 -> GS2State
- end.
-
-process_next_msg(GS2State = #gs2_state { time = Time,
- timeout_state = TimeoutState,
- queue = Queue }) ->
- case priority_queue:out(Queue) of
- {{value, Msg}, Queue1} ->
- process_msg(Msg, GS2State #gs2_state { queue = Queue1 });
- {empty, Queue1} ->
- {Time1, HibOnTimeout}
- = case {Time, TimeoutState} of
- {hibernate, {backoff, Current, _Min, _Desired, _RSt}} ->
- {Current, true};
- {hibernate, _} ->
- %% wake_hib/7 will set Time to hibernate. If
- %% we were woken and didn't receive a msg
- %% then we will get here and need a sensible
- %% value for Time1, otherwise we crash.
- %% R13B1 always waits infinitely when waking
- %% from hibernation, so that's what we do
- %% here too.
- {infinity, false};
- _ -> {Time, false}
- end,
- receive
- Input ->
- %% Time could be 'hibernate' here, so *don't* call loop
- process_next_msg(
- drain(in(Input, GS2State #gs2_state { queue = Queue1 })))
- after Time1 ->
- case HibOnTimeout of
- true ->
- pre_hibernate(
- GS2State #gs2_state { queue = Queue1 });
- false ->
- process_msg(timeout,
- GS2State #gs2_state { queue = Queue1 })
- end
- end
- end.
-
-wake_hib(GS2State = #gs2_state { timeout_state = TS }) ->
- TimeoutState1 = case TS of
- undefined ->
- undefined;
- {SleptAt, TimeoutState} ->
- adjust_timeout_state(SleptAt,
- time_compat:monotonic_time(),
- TimeoutState)
- end,
- post_hibernate(
- drain(GS2State #gs2_state { timeout_state = TimeoutState1 })).
-
-hibernate(GS2State = #gs2_state { timeout_state = TimeoutState }) ->
- TS = case TimeoutState of
- undefined -> undefined;
- {backoff, _, _, _, _} -> {time_compat:monotonic_time(),
- TimeoutState}
- end,
- proc_lib:hibernate(?MODULE, wake_hib,
- [GS2State #gs2_state { timeout_state = TS }]).
-
-pre_hibernate(GS2State = #gs2_state { state = State,
- mod = Mod }) ->
- case erlang:function_exported(Mod, handle_pre_hibernate, 1) of
- true ->
- case catch Mod:handle_pre_hibernate(State) of
- {hibernate, NState} ->
- hibernate(GS2State #gs2_state { state = NState } );
- Reply ->
- handle_common_termination(Reply, pre_hibernate, GS2State)
- end;
- false ->
- hibernate(GS2State)
- end.
-
-post_hibernate(GS2State = #gs2_state { state = State,
- mod = Mod }) ->
- case erlang:function_exported(Mod, handle_post_hibernate, 1) of
- true ->
- case catch Mod:handle_post_hibernate(State) of
- {noreply, NState} ->
- process_next_msg(GS2State #gs2_state { state = NState,
- time = infinity });
- {noreply, NState, Time} ->
- process_next_msg(GS2State #gs2_state { state = NState,
- time = Time });
- Reply ->
- handle_common_termination(Reply, post_hibernate, GS2State)
- end;
- false ->
- %% use hibernate here, not infinity. This matches
- %% R13B. The key is that we should be able to get through
- %% to process_msg calling sys:handle_system_msg with Time
- %% still set to hibernate, iff that msg is the very msg
- %% that woke us up (or the first msg we receive after
- %% waking up).
- process_next_msg(GS2State #gs2_state { time = hibernate })
- end.
-
-adjust_timeout_state(SleptAt, AwokeAt, {backoff, CurrentTO, MinimumTO,
- DesiredHibPeriod, RandomState}) ->
- NapLengthMicros = time_compat:convert_time_unit(AwokeAt - SleptAt,
- native, micro_seconds),
- CurrentMicros = CurrentTO * 1000,
- MinimumMicros = MinimumTO * 1000,
- DesiredHibMicros = DesiredHibPeriod * 1000,
- GapBetweenMessagesMicros = NapLengthMicros + CurrentMicros,
- Base =
- %% If enough time has passed between the last two messages then we
- %% should consider sleeping sooner. Otherwise stay awake longer.
- case GapBetweenMessagesMicros > (MinimumMicros + DesiredHibMicros) of
- true -> lists:max([MinimumTO, CurrentTO div 2]);
- false -> CurrentTO
- end,
- {Extra, RandomState1} = random:uniform_s(Base, RandomState),
- CurrentTO1 = Base + Extra,
- {backoff, CurrentTO1, MinimumTO, DesiredHibPeriod, RandomState1}.
-
-in({'$gen_cast', Msg} = Input,
- GS2State = #gs2_state { prioritisers = {_, F, _} }) ->
- in(Input, F(Msg, GS2State), GS2State);
-in({'$gen_call', From, Msg} = Input,
- GS2State = #gs2_state { prioritisers = {F, _, _} }) ->
- in(Input, F(Msg, From, GS2State), GS2State);
-in({'$with_state', _From, _Fun} = Input, GS2State) ->
- in(Input, 0, GS2State);
-in({'EXIT', Parent, _R} = Input, GS2State = #gs2_state { parent = Parent }) ->
- in(Input, infinity, GS2State);
-in({system, _From, _Req} = Input, GS2State) ->
- in(Input, infinity, GS2State);
-in(Input, GS2State = #gs2_state { prioritisers = {_, _, F} }) ->
- in(Input, F(Input, GS2State), GS2State).
-
-in(_Input, drop, GS2State) ->
- GS2State;
-
-in(Input, Priority, GS2State = #gs2_state { queue = Queue }) ->
- GS2State # gs2_state { queue = priority_queue:in(Input, Priority, Queue) }.
-
-process_msg({system, From, Req},
- GS2State = #gs2_state { parent = Parent, debug = Debug }) ->
- %% gen_server puts Hib on the end as the 7th arg, but that version
- %% of the fun seems not to be documented so leaving out for now.
- sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug, GS2State);
-process_msg({'$with_state', From, Fun},
- GS2State = #gs2_state{state = State}) ->
- reply(From, catch Fun(State)),
- loop(GS2State);
-process_msg({'EXIT', Parent, Reason} = Msg,
- GS2State = #gs2_state { parent = Parent }) ->
- terminate(Reason, Msg, GS2State);
-process_msg(Msg, GS2State = #gs2_state { debug = [] }) ->
- handle_msg(Msg, GS2State);
-process_msg(Msg, GS2State = #gs2_state { name = Name, debug = Debug }) ->
- Debug1 = sys:handle_debug(Debug, fun print_event/3, Name, {in, Msg}),
- handle_msg(Msg, GS2State #gs2_state { debug = Debug1 }).
-
-%%% ---------------------------------------------------
-%%% Send/recive functions
-%%% ---------------------------------------------------
-do_send(Dest, Msg) ->
- catch erlang:send(Dest, Msg).
-
-do_multi_call(Nodes, Name, Req, infinity) ->
- Tag = make_ref(),
- Monitors = send_nodes(Nodes, Name, Tag, Req),
- rec_nodes(Tag, Monitors, Name, undefined);
-do_multi_call(Nodes, Name, Req, Timeout) ->
- Tag = make_ref(),
- Caller = self(),
- Receiver =
- spawn(
- fun () ->
- %% Middleman process. Should be unsensitive to regular
- %% exit signals. The sychronization is needed in case
- %% the receiver would exit before the caller started
- %% the monitor.
- process_flag(trap_exit, true),
- Mref = erlang:monitor(process, Caller),
- receive
- {Caller,Tag} ->
- Monitors = send_nodes(Nodes, Name, Tag, Req),
- TimerId = erlang:start_timer(Timeout, self(), ok),
- Result = rec_nodes(Tag, Monitors, Name, TimerId),
- exit({self(),Tag,Result});
- {'DOWN',Mref,_,_,_} ->
- %% Caller died before sending us the go-ahead.
- %% Give up silently.
- exit(normal)
- end
- end),
- Mref = erlang:monitor(process, Receiver),
- Receiver ! {self(),Tag},
- receive
- {'DOWN',Mref,_,_,{Receiver,Tag,Result}} ->
- Result;
- {'DOWN',Mref,_,_,Reason} ->
- %% The middleman code failed. Or someone did
- %% exit(_, kill) on the middleman process => Reason==killed
- exit(Reason)
- end.
-
-send_nodes(Nodes, Name, Tag, Req) ->
- send_nodes(Nodes, Name, Tag, Req, []).
-
-send_nodes([Node|Tail], Name, Tag, Req, Monitors)
- when is_atom(Node) ->
- Monitor = start_monitor(Node, Name),
- %% Handle non-existing names in rec_nodes.
- catch {Name, Node} ! {'$gen_call', {self(), {Tag, Node}}, Req},
- send_nodes(Tail, Name, Tag, Req, [Monitor | Monitors]);
-send_nodes([_Node|Tail], Name, Tag, Req, Monitors) ->
- %% Skip non-atom Node
- send_nodes(Tail, Name, Tag, Req, Monitors);
-send_nodes([], _Name, _Tag, _Req, Monitors) ->
- Monitors.
-
-%% Against old nodes:
-%% If no reply has been delivered within 2 secs. (per node) check that
-%% the server really exists and wait for ever for the answer.
-%%
-%% Against contemporary nodes:
-%% Wait for reply, server 'DOWN', or timeout from TimerId.
-
-rec_nodes(Tag, Nodes, Name, TimerId) ->
- rec_nodes(Tag, Nodes, Name, [], [], 2000, TimerId).
-
-rec_nodes(Tag, [{N,R}|Tail], Name, Badnodes, Replies, Time, TimerId ) ->
- receive
- {'DOWN', R, _, _, _} ->
- rec_nodes(Tag, Tail, Name, [N|Badnodes], Replies, Time, TimerId);
- {{Tag, N}, Reply} -> %% Tag is bound !!!
- unmonitor(R),
- rec_nodes(Tag, Tail, Name, Badnodes,
- [{N,Reply}|Replies], Time, TimerId);
- {timeout, TimerId, _} ->
- unmonitor(R),
- %% Collect all replies that already have arrived
- rec_nodes_rest(Tag, Tail, Name, [N|Badnodes], Replies)
- end;
-rec_nodes(Tag, [N|Tail], Name, Badnodes, Replies, Time, TimerId) ->
- %% R6 node
- receive
- {nodedown, N} ->
- monitor_node(N, false),
- rec_nodes(Tag, Tail, Name, [N|Badnodes], Replies, 2000, TimerId);
- {{Tag, N}, Reply} -> %% Tag is bound !!!
- receive {nodedown, N} -> ok after 0 -> ok end,
- monitor_node(N, false),
- rec_nodes(Tag, Tail, Name, Badnodes,
- [{N,Reply}|Replies], 2000, TimerId);
- {timeout, TimerId, _} ->
- receive {nodedown, N} -> ok after 0 -> ok end,
- monitor_node(N, false),
- %% Collect all replies that already have arrived
- rec_nodes_rest(Tag, Tail, Name, [N | Badnodes], Replies)
- after Time ->
- case rpc:call(N, erlang, whereis, [Name]) of
- Pid when is_pid(Pid) -> % It exists try again.
- rec_nodes(Tag, [N|Tail], Name, Badnodes,
- Replies, infinity, TimerId);
- _ -> % badnode
- receive {nodedown, N} -> ok after 0 -> ok end,
- monitor_node(N, false),
- rec_nodes(Tag, Tail, Name, [N|Badnodes],
- Replies, 2000, TimerId)
- end
- end;
-rec_nodes(_, [], _, Badnodes, Replies, _, TimerId) ->
- case catch erlang:cancel_timer(TimerId) of
- false -> % It has already sent it's message
- receive
- {timeout, TimerId, _} -> ok
- after 0 ->
- ok
- end;
- _ -> % Timer was cancelled, or TimerId was 'undefined'
- ok
- end,
- {Replies, Badnodes}.
-
-%% Collect all replies that already have arrived
-rec_nodes_rest(Tag, [{N,R}|Tail], Name, Badnodes, Replies) ->
- receive
- {'DOWN', R, _, _, _} ->
- rec_nodes_rest(Tag, Tail, Name, [N|Badnodes], Replies);
- {{Tag, N}, Reply} -> %% Tag is bound !!!
- unmonitor(R),
- rec_nodes_rest(Tag, Tail, Name, Badnodes, [{N,Reply}|Replies])
- after 0 ->
- unmonitor(R),
- rec_nodes_rest(Tag, Tail, Name, [N|Badnodes], Replies)
- end;
-rec_nodes_rest(Tag, [N|Tail], Name, Badnodes, Replies) ->
- %% R6 node
- receive
- {nodedown, N} ->
- monitor_node(N, false),
- rec_nodes_rest(Tag, Tail, Name, [N|Badnodes], Replies);
- {{Tag, N}, Reply} -> %% Tag is bound !!!
- receive {nodedown, N} -> ok after 0 -> ok end,
- monitor_node(N, false),
- rec_nodes_rest(Tag, Tail, Name, Badnodes, [{N,Reply}|Replies])
- after 0 ->
- receive {nodedown, N} -> ok after 0 -> ok end,
- monitor_node(N, false),
- rec_nodes_rest(Tag, Tail, Name, [N|Badnodes], Replies)
- end;
-rec_nodes_rest(_Tag, [], _Name, Badnodes, Replies) ->
- {Replies, Badnodes}.
-
-
-%%% ---------------------------------------------------
-%%% Monitor functions
-%%% ---------------------------------------------------
-
-start_monitor(Node, Name) when is_atom(Node), is_atom(Name) ->
- if node() =:= nonode@nohost, Node =/= nonode@nohost ->
- Ref = make_ref(),
- self() ! {'DOWN', Ref, process, {Name, Node}, noconnection},
- {Node, Ref};
- true ->
- case catch erlang:monitor(process, {Name, Node}) of
- {'EXIT', _} ->
- %% Remote node is R6
- monitor_node(Node, true),
- Node;
- Ref when is_reference(Ref) ->
- {Node, Ref}
- end
- end.
-
-%% Cancels a monitor started with Ref=erlang:monitor(_, _).
-unmonitor(Ref) when is_reference(Ref) ->
- erlang:demonitor(Ref),
- receive
- {'DOWN', Ref, _, _, _} ->
- true
- after 0 ->
- true
- end.
-
-%%% ---------------------------------------------------
-%%% Message handling functions
-%%% ---------------------------------------------------
-
-dispatch({'$gen_cast', Msg}, Mod, State) ->
- Mod:handle_cast(Msg, State);
-dispatch(Info, Mod, State) ->
- Mod:handle_info(Info, State).
-
-common_reply(_Name, From, Reply, _NState, [] = _Debug) ->
- reply(From, Reply),
- [];
-common_reply(Name, {To, _Tag} = From, Reply, NState, Debug) ->
- reply(From, Reply),
- sys:handle_debug(Debug, fun print_event/3, Name, {out, Reply, To, NState}).
-
-common_noreply(_Name, _NState, [] = _Debug) ->
- [];
-common_noreply(Name, NState, Debug) ->
- sys:handle_debug(Debug, fun print_event/3, Name, {noreply, NState}).
-
-common_become(_Name, _Mod, _NState, [] = _Debug) ->
- [];
-common_become(Name, Mod, NState, Debug) ->
- sys:handle_debug(Debug, fun print_event/3, Name, {become, Mod, NState}).
-
-handle_msg({'$gen_call', From, Msg}, GS2State = #gs2_state { mod = Mod,
- state = State,
- name = Name,
- debug = Debug }) ->
- case catch Mod:handle_call(Msg, From, State) of
- {reply, Reply, NState} ->
- Debug1 = common_reply(Name, From, Reply, NState, Debug),
- loop(GS2State #gs2_state { state = NState,
- time = infinity,
- debug = Debug1 });
- {reply, Reply, NState, Time1} ->
- Debug1 = common_reply(Name, From, Reply, NState, Debug),
- loop(GS2State #gs2_state { state = NState,
- time = Time1,
- debug = Debug1});
- {stop, Reason, Reply, NState} ->
- {'EXIT', R} =
- (catch terminate(Reason, Msg,
- GS2State #gs2_state { state = NState })),
- common_reply(Name, From, Reply, NState, Debug),
- exit(R);
- Other ->
- handle_common_reply(Other, Msg, GS2State)
- end;
-handle_msg(Msg, GS2State = #gs2_state { mod = Mod, state = State }) ->
- Reply = (catch dispatch(Msg, Mod, State)),
- handle_common_reply(Reply, Msg, GS2State).
-
-handle_common_reply(Reply, Msg, GS2State = #gs2_state { name = Name,
- debug = Debug}) ->
- case Reply of
- {noreply, NState} ->
- Debug1 = common_noreply(Name, NState, Debug),
- loop(GS2State #gs2_state {state = NState,
- time = infinity,
- debug = Debug1});
- {noreply, NState, Time1} ->
- Debug1 = common_noreply(Name, NState, Debug),
- loop(GS2State #gs2_state {state = NState,
- time = Time1,
- debug = Debug1});
- {become, Mod, NState} ->
- Debug1 = common_become(Name, Mod, NState, Debug),
- loop(find_prioritisers(
- GS2State #gs2_state { mod = Mod,
- state = NState,
- time = infinity,
- debug = Debug1 }));
- {become, Mod, NState, Time1} ->
- Debug1 = common_become(Name, Mod, NState, Debug),
- loop(find_prioritisers(
- GS2State #gs2_state { mod = Mod,
- state = NState,
- time = Time1,
- debug = Debug1 }));
- _ ->
- handle_common_termination(Reply, Msg, GS2State)
- end.
-
-handle_common_termination(Reply, Msg, GS2State) ->
- case Reply of
- {stop, Reason, NState} ->
- terminate(Reason, Msg, GS2State #gs2_state { state = NState });
- {'EXIT', What} ->
- terminate(What, Msg, GS2State);
- _ ->
- terminate({bad_return_value, Reply}, Msg, GS2State)
- end.
-
-%%-----------------------------------------------------------------
-%% Callback functions for system messages handling.
-%%-----------------------------------------------------------------
-system_continue(Parent, Debug, GS2State) ->
- loop(GS2State #gs2_state { parent = Parent, debug = Debug }).
-
-system_terminate(Reason, _Parent, Debug, GS2State) ->
- terminate(Reason, [], GS2State #gs2_state { debug = Debug }).
-
-system_code_change(GS2State = #gs2_state { mod = Mod,
- state = State },
- _Module, OldVsn, Extra) ->
- case catch Mod:code_change(OldVsn, State, Extra) of
- {ok, NewState} ->
- NewGS2State = find_prioritisers(
- GS2State #gs2_state { state = NewState }),
- {ok, [NewGS2State]};
- Else ->
- Else
- end.
-
-%%-----------------------------------------------------------------
-%% Format debug messages. Print them as the call-back module sees
-%% them, not as the real erlang messages. Use trace for that.
-%%-----------------------------------------------------------------
-print_event(Dev, {in, Msg}, Name) ->
- case Msg of
- {'$gen_call', {From, _Tag}, Call} ->
- io:format(Dev, "*DBG* ~p got call ~p from ~w~n",
- [Name, Call, From]);
- {'$gen_cast', Cast} ->
- io:format(Dev, "*DBG* ~p got cast ~p~n",
- [Name, Cast]);
- _ ->
- io:format(Dev, "*DBG* ~p got ~p~n", [Name, Msg])
- end;
-print_event(Dev, {out, Msg, To, State}, Name) ->
- io:format(Dev, "*DBG* ~p sent ~p to ~w, new state ~w~n",
- [Name, Msg, To, State]);
-print_event(Dev, {noreply, State}, Name) ->
- io:format(Dev, "*DBG* ~p new state ~w~n", [Name, State]);
-print_event(Dev, Event, Name) ->
- io:format(Dev, "*DBG* ~p dbg ~p~n", [Name, Event]).
-
-
-%%% ---------------------------------------------------
-%%% Terminate the server.
-%%% ---------------------------------------------------
-
-terminate(Reason, Msg, #gs2_state { name = Name,
- mod = Mod,
- state = State,
- debug = Debug }) ->
- case catch Mod:terminate(Reason, State) of
- {'EXIT', R} ->
- error_info(R, Reason, Name, Msg, State, Debug),
- exit(R);
- _ ->
- case Reason of
- normal ->
- exit(normal);
- shutdown ->
- exit(shutdown);
- {shutdown,_}=Shutdown ->
- exit(Shutdown);
- _ ->
- error_info(Reason, undefined, Name, Msg, State, Debug),
- exit(Reason)
- end
- end.
-
-error_info(_Reason, _RootCause, application_controller, _Msg, _State, _Debug) ->
- %% OTP-5811 Don't send an error report if it's the system process
- %% application_controller which is terminating - let init take care
- %% of it instead
- ok;
-error_info(Reason, RootCause, Name, Msg, State, Debug) ->
- Reason1 = error_reason(Reason),
- Fmt =
- "** Generic server ~p terminating~n"
- "** Last message in was ~p~n"
- "** When Server state == ~p~n"
- "** Reason for termination == ~n** ~p~n",
- case RootCause of
- undefined -> format(Fmt, [Name, Msg, State, Reason1]);
- _ -> format(Fmt ++ "** In 'terminate' callback "
- "with reason ==~n** ~p~n",
- [Name, Msg, State, Reason1,
- error_reason(RootCause)])
- end,
- sys:print_log(Debug),
- ok.
-
-error_reason({undef,[{M,F,A}|MFAs]} = Reason) ->
- case code:is_loaded(M) of
- false -> {'module could not be loaded',[{M,F,A}|MFAs]};
- _ -> case erlang:function_exported(M, F, length(A)) of
- true -> Reason;
- false -> {'function not exported',[{M,F,A}|MFAs]}
- end
- end;
-error_reason(Reason) ->
- Reason.
-
-%%% ---------------------------------------------------
-%%% Misc. functions.
-%%% ---------------------------------------------------
-
-opt(Op, [{Op, Value}|_]) ->
- {ok, Value};
-opt(Op, [_|Options]) ->
- opt(Op, Options);
-opt(_, []) ->
- false.
-
-debug_options(Name, Opts) ->
- case opt(debug, Opts) of
- {ok, Options} -> dbg_options(Name, Options);
- _ -> dbg_options(Name, [])
- end.
-
-dbg_options(Name, []) ->
- Opts =
- case init:get_argument(generic_debug) of
- error ->
- [];
- _ ->
- [log, statistics]
- end,
- dbg_opts(Name, Opts);
-dbg_options(Name, Opts) ->
- dbg_opts(Name, Opts).
-
-dbg_opts(Name, Opts) ->
- case catch sys:debug_options(Opts) of
- {'EXIT',_} ->
- format("~p: ignoring erroneous debug options - ~p~n",
- [Name, Opts]),
- [];
- Dbg ->
- Dbg
- end.
-
-get_proc_name(Pid) when is_pid(Pid) ->
- Pid;
-get_proc_name({local, Name}) ->
- case process_info(self(), registered_name) of
- {registered_name, Name} ->
- Name;
- {registered_name, _Name} ->
- exit(process_not_registered);
- [] ->
- exit(process_not_registered)
- end;
-get_proc_name({global, Name}) ->
- case whereis_name(Name) of
- undefined ->
- exit(process_not_registered_globally);
- Pid when Pid =:= self() ->
- Name;
- _Pid ->
- exit(process_not_registered_globally)
- end.
-
-get_parent() ->
- case get('$ancestors') of
- [Parent | _] when is_pid(Parent)->
- Parent;
- [Parent | _] when is_atom(Parent)->
- name_to_pid(Parent);
- _ ->
- exit(process_was_not_started_by_proc_lib)
- end.
-
-name_to_pid(Name) ->
- case whereis(Name) of
- undefined ->
- case whereis_name(Name) of
- undefined ->
- exit(could_not_find_registerd_name);
- Pid ->
- Pid
- end;
- Pid ->
- Pid
- end.
-
-whereis_name(Name) ->
- case ets:lookup(global_names, Name) of
- [{_Name, Pid, _Method, _RPid, _Ref}] ->
- if node(Pid) == node() ->
- case is_process_alive(Pid) of
- true -> Pid;
- false -> undefined
- end;
- true ->
- Pid
- end;
- [] -> undefined
- end.
-
-find_prioritisers(GS2State = #gs2_state { mod = Mod }) ->
- PCall = function_exported_or_default(Mod, 'prioritise_call', 4,
- fun (_Msg, _From, _State) -> 0 end),
- PCast = function_exported_or_default(Mod, 'prioritise_cast', 3,
- fun (_Msg, _State) -> 0 end),
- PInfo = function_exported_or_default(Mod, 'prioritise_info', 3,
- fun (_Msg, _State) -> 0 end),
- GS2State #gs2_state { prioritisers = {PCall, PCast, PInfo} }.
-
-function_exported_or_default(Mod, Fun, Arity, Default) ->
- case erlang:function_exported(Mod, Fun, Arity) of
- true -> case Arity of
- 3 -> fun (Msg, GS2State = #gs2_state { queue = Queue,
- state = State }) ->
- Length = priority_queue:len(Queue),
- case catch Mod:Fun(Msg, Length, State) of
- drop ->
- drop;
- Res when is_integer(Res) ->
- Res;
- Err ->
- handle_common_termination(Err, Msg, GS2State)
- end
- end;
- 4 -> fun (Msg, From, GS2State = #gs2_state { queue = Queue,
- state = State }) ->
- Length = priority_queue:len(Queue),
- case catch Mod:Fun(Msg, From, Length, State) of
- Res when is_integer(Res) ->
- Res;
- Err ->
- handle_common_termination(Err, Msg, GS2State)
- end
- end
- end;
- false -> Default
- end.
-
-%%-----------------------------------------------------------------
-%% Status information
-%%-----------------------------------------------------------------
-format_status(Opt, StatusData) ->
- [PDict, SysState, Parent, Debug,
- #gs2_state{name = Name, state = State, mod = Mod, queue = Queue}] =
- StatusData,
- NameTag = if is_pid(Name) ->
- pid_to_list(Name);
- is_atom(Name) ->
- Name
- end,
- Header = lists:concat(["Status for generic server ", NameTag]),
- Log = sys:get_debug(log, Debug, []),
- Specfic = callback(Mod, format_status, [Opt, [PDict, State]],
- fun () -> [{data, [{"State", State}]}] end),
- Messages = callback(Mod, format_message_queue, [Opt, Queue],
- fun () -> priority_queue:to_list(Queue) end),
- [{header, Header},
- {data, [{"Status", SysState},
- {"Parent", Parent},
- {"Logged events", Log},
- {"Queued messages", Messages}]} |
- Specfic].
-
-callback(Mod, FunName, Args, DefaultThunk) ->
- case erlang:function_exported(Mod, FunName, length(Args)) of
- true -> case catch apply(Mod, FunName, Args) of
- {'EXIT', _} -> DefaultThunk();
- Success -> Success
- end;
- false -> DefaultThunk()
- end.
diff --git a/src/mirrored_supervisor.erl b/src/mirrored_supervisor.erl
deleted file mode 100644
index 96c1418791..0000000000
--- a/src/mirrored_supervisor.erl
+++ /dev/null
@@ -1,517 +0,0 @@
-%% The contents of this file are subject to the Mozilla Public License
-%% Version 1.1 (the "License"); you may not use this file except in
-%% compliance with the License. You may obtain a copy of the License at
-%% http://www.mozilla.org/MPL/
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
-%% License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% The Original Code is RabbitMQ.
-%%
-%% The Initial Developer of the Original Code is GoPivotal, Inc.
-%% Copyright (c) 2011-2015 Pivotal Software, Inc. All rights reserved.
-%%
-
--module(mirrored_supervisor).
-
-%% Mirrored Supervisor
-%% ===================
-%%
-%% This module implements a new type of supervisor. It acts like a
-%% normal supervisor, but at creation time you also provide the name
-%% of a process group to join. All the supervisors within the
-%% process group act like a single large distributed supervisor:
-%%
-%% * A process with a given child_id will only exist on one
-%% supervisor within the group.
-%%
-%% * If one supervisor fails, children may migrate to surviving
-%% supervisors within the group.
-%%
-%% In almost all cases you will want to use the module name for the
-%% process group. Using multiple process groups with the same module
-%% name is supported. Having multiple module names for the same
-%% process group will lead to undefined behaviour.
-%%
-%% Motivation
-%% ----------
-%%
-%% Sometimes you have processes which:
-%%
-%% * Only need to exist once per cluster.
-%%
-%% * Does not contain much state (or can reconstruct its state easily).
-%%
-%% * Needs to be restarted elsewhere should it be running on a node
-%% which fails.
-%%
-%% By creating a mirrored supervisor group with one supervisor on
-%% each node, that's what you get.
-%%
-%%
-%% API use
-%% -------
-%%
-%% This is basically the same as for supervisor, except that:
-%%
-%% 1) start_link(Module, Args) becomes
-%% start_link(Group, TxFun, Module, Args).
-%%
-%% 2) start_link({local, Name}, Module, Args) becomes
-%% start_link({local, Name}, Group, TxFun, Module, Args).
-%%
-%% 3) start_link({global, Name}, Module, Args) is not available.
-%%
-%% 4) The restart strategy simple_one_for_one is not available.
-%%
-%% 5) Mnesia is used to hold global state. At some point your
-%% application should invoke create_tables() (or table_definitions()
-%% if it wants to manage table creation itself).
-%%
-%% The TxFun parameter to start_link/{4,5} is a function which the
-%% mirrored supervisor can use to execute Mnesia transactions. In the
-%% RabbitMQ server this goes via a worker pool; in other cases a
-%% function like:
-%%
-%% tx_fun(Fun) ->
-%% case mnesia:sync_transaction(Fun) of
-%% {atomic, Result} -> Result;
-%% {aborted, Reason} -> throw({error, Reason})
-%% end.
-%%
-%% could be used.
-%%
-%% Internals
-%% ---------
-%%
-%% Each mirrored_supervisor consists of three processes - the overall
-%% supervisor, the delegate supervisor and the mirroring server. The
-%% overall supervisor supervises the other two processes. Its pid is
-%% the one returned from start_link; the pids of the other two
-%% processes are effectively hidden in the API.
-%%
-%% The delegate supervisor is in charge of supervising all the child
-%% processes that are added to the supervisor as usual.
-%%
-%% The mirroring server intercepts calls to the supervisor API
-%% (directed at the overall supervisor), does any special handling,
-%% and forwards everything to the delegate supervisor.
-%%
-%% This module implements all three, hence init/1 is somewhat overloaded.
-%%
-%% The mirroring server creates and joins a process group on
-%% startup. It monitors all the existing members of this group, and
-%% broadcasts a "hello" message to them so that they can monitor it in
-%% turn. When it receives a 'DOWN' message, it checks to see if it's
-%% the "first" server in the group and restarts all the child
-%% processes from the dead supervisor if so.
-%%
-%% In the future we might load balance this.
-%%
-%% Startup is slightly fiddly. The mirroring server needs to know the
-%% Pid of the overall supervisor, but we don't have that until it has
-%% started. Therefore we set this after the fact. We also start any
-%% children we found in Module:init() at this point, since starting
-%% children requires knowing the overall supervisor pid.
-
--define(SUPERVISOR, supervisor2).
--define(GEN_SERVER, gen_server2).
--define(PG2, pg2_fixed).
--define(SUP_MODULE, mirrored_supervisor_sups).
-
--define(TABLE, mirrored_sup_childspec).
--define(TABLE_DEF,
- {?TABLE,
- [{record_name, mirrored_sup_childspec},
- {type, ordered_set},
- {attributes, record_info(fields, mirrored_sup_childspec)}]}).
--define(TABLE_MATCH, {match, #mirrored_sup_childspec{ _ = '_' }}).
-
--export([start_link/4, start_link/5,
- start_child/2, restart_child/2,
- delete_child/2, terminate_child/2,
- which_children/1, count_children/1, check_childspecs/1]).
-
--behaviour(?GEN_SERVER).
-
--export([init/1, handle_call/3, handle_info/2, terminate/2, code_change/3,
- handle_cast/2]).
-
--export([start_internal/3]).
--export([create_tables/0, table_definitions/0]).
-
--record(mirrored_sup_childspec, {key, mirroring_pid, childspec}).
-
--record(state, {overall,
- delegate,
- group,
- tx_fun,
- initial_childspecs}).
-
-%%----------------------------------------------------------------------------
-
--ifdef(use_specs).
-
-%%--------------------------------------------------------------------------
-%% Callback behaviour
-%%--------------------------------------------------------------------------
-
--callback init(Args :: term()) ->
- {ok, {{RestartStrategy :: supervisor2:strategy(),
- MaxR :: non_neg_integer(),
- MaxT :: non_neg_integer()},
- [ChildSpec :: supervisor2:child_spec()]}}
- | ignore.
-
-%%--------------------------------------------------------------------------
-%% Specs
-%%--------------------------------------------------------------------------
-
--type startlink_err() :: {'already_started', pid()} | 'shutdown' | term().
--type startlink_ret() :: {'ok', pid()} | 'ignore' | {'error', startlink_err()}.
-
--type group_name() :: any().
-
--type(tx_fun() :: fun((fun(() -> A)) -> A)).
-
--spec start_link(GroupName, TxFun, Module, Args) -> startlink_ret() when
- GroupName :: group_name(),
- TxFun :: tx_fun(),
- Module :: module(),
- Args :: term().
-
--spec start_link(SupName, GroupName, TxFun, Module, Args) ->
- startlink_ret() when
- SupName :: supervisor2:sup_name(),
- GroupName :: group_name(),
- TxFun :: tx_fun(),
- Module :: module(),
- Args :: term().
-
--spec start_internal(Group, TxFun, ChildSpecs) -> Result when
- Group :: group_name(),
- TxFun :: tx_fun(),
- ChildSpecs :: [supervisor2:child_spec()],
- Result :: {'ok', pid()} | {'error', term()}.
-
--spec create_tables() -> Result when
- Result :: 'ok'.
-
--else.
-
--export([behaviour_info/1]).
-
-behaviour_info(callbacks) -> [{init,1}];
-behaviour_info(_Other) -> undefined.
-
--endif.
-
-%%----------------------------------------------------------------------------
-
-start_link(Group, TxFun, Mod, Args) ->
- start_link0([], Group, TxFun, init(Mod, Args)).
-
-start_link({local, SupName}, Group, TxFun, Mod, Args) ->
- start_link0([{local, SupName}], Group, TxFun, init(Mod, Args));
-
-start_link({global, _SupName}, _Group, _TxFun, _Mod, _Args) ->
- erlang:error(badarg).
-
-start_link0(Prefix, Group, TxFun, Init) ->
- case apply(?SUPERVISOR, start_link,
- Prefix ++ [?SUP_MODULE, {overall, Group, TxFun, Init}]) of
- {ok, Pid} -> case catch call(Pid, {init, Pid}) of
- ok -> {ok, Pid};
- E -> E
- end;
- Other -> Other
- end.
-
-init(Mod, Args) ->
- case Mod:init(Args) of
- {ok, {{Bad, _, _}, _ChildSpecs}} when
- Bad =:= simple_one_for_one -> erlang:error(badarg);
- Init -> Init
- end.
-
-start_child(Sup, ChildSpec) -> call(Sup, {start_child, ChildSpec}).
-delete_child(Sup, Id) -> find_call(Sup, Id, {delete_child, Id}).
-restart_child(Sup, Id) -> find_call(Sup, Id, {msg, restart_child, [Id]}).
-terminate_child(Sup, Id) -> find_call(Sup, Id, {msg, terminate_child, [Id]}).
-which_children(Sup) -> fold(which_children, Sup, fun lists:append/2).
-count_children(Sup) -> fold(count_children, Sup, fun add_proplists/2).
-check_childspecs(Specs) -> ?SUPERVISOR:check_childspecs(Specs).
-
-call(Sup, Msg) -> ?GEN_SERVER:call(mirroring(Sup), Msg, infinity).
-cast(Sup, Msg) -> with_exit_handler(
- fun() -> ok end,
- fun() -> ?GEN_SERVER:cast(mirroring(Sup), Msg) end).
-
-find_call(Sup, Id, Msg) ->
- Group = call(Sup, group),
- MatchHead = #mirrored_sup_childspec{mirroring_pid = '$1',
- key = {Group, Id},
- _ = '_'},
- %% If we did this inside a tx we could still have failover
- %% immediately after the tx - we can't be 100% here. So we may as
- %% well dirty_select.
- case mnesia:dirty_select(?TABLE, [{MatchHead, [], ['$1']}]) of
- [Mirror] -> call(Mirror, Msg);
- [] -> {error, not_found}
- end.
-
-fold(FunAtom, Sup, AggFun) ->
- Group = call(Sup, group),
- lists:foldl(AggFun, [],
- [apply(?SUPERVISOR, FunAtom, [D]) ||
- M <- ?PG2:get_members(Group),
- D <- [delegate(M)]]).
-
-child(Sup, Id) ->
- [Pid] = [Pid || {Id1, Pid, _, _} <- ?SUPERVISOR:which_children(Sup),
- Id1 =:= Id],
- Pid.
-
-delegate(Sup) -> child(Sup, delegate).
-mirroring(Sup) -> child(Sup, mirroring).
-
-%%----------------------------------------------------------------------------
-
-start_internal(Group, TxFun, ChildSpecs) ->
- ?GEN_SERVER:start_link(?MODULE, {Group, TxFun, ChildSpecs},
- [{timeout, infinity}]).
-
-%%----------------------------------------------------------------------------
-
-init({Group, TxFun, ChildSpecs}) ->
- {ok, #state{group = Group,
- tx_fun = TxFun,
- initial_childspecs = ChildSpecs}}.
-
-handle_call({init, Overall}, _From,
- State = #state{overall = undefined,
- delegate = undefined,
- group = Group,
- tx_fun = TxFun,
- initial_childspecs = ChildSpecs}) ->
- process_flag(trap_exit, true),
- ?PG2:create(Group),
- ok = ?PG2:join(Group, Overall),
- Rest = ?PG2:get_members(Group) -- [Overall],
- case Rest of
- [] -> TxFun(fun() -> delete_all(Group) end);
- _ -> ok
- end,
- [begin
- ?GEN_SERVER:cast(mirroring(Pid), {ensure_monitoring, Overall}),
- erlang:monitor(process, Pid)
- end || Pid <- Rest],
- Delegate = delegate(Overall),
- erlang:monitor(process, Delegate),
- State1 = State#state{overall = Overall, delegate = Delegate},
- case errors([maybe_start(Group, TxFun, Overall, Delegate, S)
- || S <- ChildSpecs]) of
- [] -> {reply, ok, State1};
- Errors -> {stop, {shutdown, Errors}, State1}
- end;
-
-handle_call({start_child, ChildSpec}, _From,
- State = #state{overall = Overall,
- delegate = Delegate,
- group = Group,
- tx_fun = TxFun}) ->
- {reply, case maybe_start(Group, TxFun, Overall, Delegate, ChildSpec) of
- already_in_mnesia -> {error, already_present};
- {already_in_mnesia, Pid} -> {error, {already_started, Pid}};
- Else -> Else
- end, State};
-
-handle_call({delete_child, Id}, _From, State = #state{delegate = Delegate,
- group = Group,
- tx_fun = TxFun}) ->
- {reply, stop(Group, TxFun, Delegate, Id), State};
-
-handle_call({msg, F, A}, _From, State = #state{delegate = Delegate}) ->
- {reply, apply(?SUPERVISOR, F, [Delegate | A]), State};
-
-handle_call(group, _From, State = #state{group = Group}) ->
- {reply, Group, State};
-
-handle_call(Msg, _From, State) ->
- {stop, {unexpected_call, Msg}, State}.
-
-handle_cast({ensure_monitoring, Pid}, State) ->
- erlang:monitor(process, Pid),
- {noreply, State};
-
-handle_cast({die, Reason}, State = #state{group = Group}) ->
- tell_all_peers_to_die(Group, Reason),
- {stop, Reason, State};
-
-handle_cast(Msg, State) ->
- {stop, {unexpected_cast, Msg}, State}.
-
-handle_info({'DOWN', _Ref, process, Pid, Reason},
- State = #state{delegate = Pid, group = Group}) ->
- %% Since the delegate is temporary, its death won't cause us to
- %% die. Since the overall supervisor kills processes in reverse
- %% order when shutting down "from above" and we started after the
- %% delegate, if we see the delegate die then that means it died
- %% "from below" i.e. due to the behaviour of its children, not
- %% because the whole app was being torn down.
- %%
- %% Therefore if we get here we know we need to cause the entire
- %% mirrored sup to shut down, not just fail over.
- tell_all_peers_to_die(Group, Reason),
- {stop, Reason, State};
-
-handle_info({'DOWN', _Ref, process, Pid, _Reason},
- State = #state{delegate = Delegate,
- group = Group,
- tx_fun = TxFun,
- overall = O}) ->
- %% TODO load balance this
- %% No guarantee pg2 will have received the DOWN before us.
- R = case lists:sort(?PG2:get_members(Group)) -- [Pid] of
- [O | _] -> ChildSpecs =
- TxFun(fun() -> update_all(O, Pid) end),
- [start(Delegate, ChildSpec) || ChildSpec <- ChildSpecs];
- _ -> []
- end,
- case errors(R) of
- [] -> {noreply, State};
- Errors -> {stop, {shutdown, Errors}, State}
- end;
-
-handle_info(Info, State) ->
- {stop, {unexpected_info, Info}, State}.
-
-terminate(_Reason, _State) ->
- ok.
-
-code_change(_OldVsn, State, _Extra) ->
- {ok, State}.
-
-%%----------------------------------------------------------------------------
-
-tell_all_peers_to_die(Group, Reason) ->
- [cast(P, {die, Reason}) || P <- ?PG2:get_members(Group) -- [self()]].
-
-maybe_start(Group, TxFun, Overall, Delegate, ChildSpec) ->
- try TxFun(fun() -> check_start(Group, Overall, Delegate, ChildSpec) end) of
- start -> start(Delegate, ChildSpec);
- undefined -> already_in_mnesia;
- Pid -> {already_in_mnesia, Pid}
- catch
- %% If we are torn down while in the transaction...
- {error, E} -> {error, E}
- end.
-
-check_start(Group, Overall, Delegate, ChildSpec) ->
- case mnesia:wread({?TABLE, {Group, id(ChildSpec)}}) of
- [] -> write(Group, Overall, ChildSpec),
- start;
- [S] -> #mirrored_sup_childspec{key = {Group, Id},
- mirroring_pid = Pid} = S,
- case Overall of
- Pid -> child(Delegate, Id);
- _ -> case supervisor(Pid) of
- dead -> write(Group, Overall, ChildSpec),
- start;
- Delegate0 -> child(Delegate0, Id)
- end
- end
- end.
-
-supervisor(Pid) -> with_exit_handler(fun() -> dead end,
- fun() -> delegate(Pid) end).
-
-write(Group, Overall, ChildSpec) ->
- S = #mirrored_sup_childspec{key = {Group, id(ChildSpec)},
- mirroring_pid = Overall,
- childspec = ChildSpec},
- ok = mnesia:write(?TABLE, S, write),
- ChildSpec.
-
-delete(Group, Id) ->
- ok = mnesia:delete({?TABLE, {Group, Id}}).
-
-start(Delegate, ChildSpec) ->
- apply(?SUPERVISOR, start_child, [Delegate, ChildSpec]).
-
-stop(Group, TxFun, Delegate, Id) ->
- try TxFun(fun() -> check_stop(Group, Delegate, Id) end) of
- deleted -> apply(?SUPERVISOR, delete_child, [Delegate, Id]);
- running -> {error, running}
- catch
- {error, E} -> {error, E}
- end.
-
-check_stop(Group, Delegate, Id) ->
- case child(Delegate, Id) of
- undefined -> delete(Group, Id),
- deleted;
- _ -> running
- end.
-
-id({Id, _, _, _, _, _}) -> Id.
-
-update_all(Overall, OldOverall) ->
- MatchHead = #mirrored_sup_childspec{mirroring_pid = OldOverall,
- key = '$1',
- childspec = '$2',
- _ = '_'},
- [write(Group, Overall, C) ||
- [{Group, _Id}, C] <- mnesia:select(?TABLE, [{MatchHead, [], ['$$']}])].
-
-delete_all(Group) ->
- MatchHead = #mirrored_sup_childspec{key = {Group, '_'},
- childspec = '$1',
- _ = '_'},
- [delete(Group, id(C)) ||
- C <- mnesia:select(?TABLE, [{MatchHead, [], ['$1']}])].
-
-errors(Results) -> [E || {error, E} <- Results].
-
-%%----------------------------------------------------------------------------
-
-create_tables() -> create_tables([?TABLE_DEF]).
-
-create_tables([]) ->
- ok;
-create_tables([{Table, Attributes} | Ts]) ->
- case mnesia:create_table(Table, Attributes) of
- {atomic, ok} -> create_tables(Ts);
- {aborted, {already_exists, ?TABLE}} -> create_tables(Ts);
- Err -> Err
- end.
-
-table_definitions() ->
- {Name, Attributes} = ?TABLE_DEF,
- [{Name, [?TABLE_MATCH | Attributes]}].
-
-%%----------------------------------------------------------------------------
-
-with_exit_handler(Handler, Thunk) ->
- try
- Thunk()
- catch
- exit:{R, _} when R =:= noproc; R =:= nodedown;
- R =:= normal; R =:= shutdown ->
- Handler();
- exit:{{R, _}, _} when R =:= nodedown; R =:= shutdown ->
- Handler()
- end.
-
-add_proplists(P1, P2) ->
- add_proplists(lists:keysort(1, P1), lists:keysort(1, P2), []).
-add_proplists([], P2, Acc) -> P2 ++ Acc;
-add_proplists(P1, [], Acc) -> P1 ++ Acc;
-add_proplists([{K, V1} | P1], [{K, V2} | P2], Acc) ->
- add_proplists(P1, P2, [{K, V1 + V2} | Acc]);
-add_proplists([{K1, _} = KV | P1], [{K2, _} | _] = P2, Acc) when K1 < K2 ->
- add_proplists(P1, P2, [KV | Acc]);
-add_proplists(P1, [KV | P2], Acc) ->
- add_proplists(P1, P2, [KV | Acc]).
diff --git a/src/mochijson2.erl b/src/mochijson2.erl
deleted file mode 100644
index bddb52cc6f..0000000000
--- a/src/mochijson2.erl
+++ /dev/null
@@ -1,893 +0,0 @@
-%% This file is a copy of `mochijson2.erl' from mochiweb, revision
-%% d541e9a0f36c00dcadc2e589f20e47fbf46fc76f. For the license, see
-%% `LICENSE-MIT-Mochi'.
-
-%% @author Bob Ippolito <bob@mochimedia.com>
-%% @copyright 2007 Mochi Media, Inc.
-
-%% @doc Yet another JSON (RFC 4627) library for Erlang. mochijson2 works
-%% with binaries as strings, arrays as lists (without an {array, _})
-%% wrapper and it only knows how to decode UTF-8 (and ASCII).
-%%
-%% JSON terms are decoded as follows (javascript -> erlang):
-%% <ul>
-%% <li>{"key": "value"} ->
-%% {struct, [{&lt;&lt;"key">>, &lt;&lt;"value">>}]}</li>
-%% <li>["array", 123, 12.34, true, false, null] ->
-%% [&lt;&lt;"array">>, 123, 12.34, true, false, null]
-%% </li>
-%% </ul>
-%% <ul>
-%% <li>Strings in JSON decode to UTF-8 binaries in Erlang</li>
-%% <li>Objects decode to {struct, PropList}</li>
-%% <li>Numbers decode to integer or float</li>
-%% <li>true, false, null decode to their respective terms.</li>
-%% </ul>
-%% The encoder will accept the same format that the decoder will produce,
-%% but will also allow additional cases for leniency:
-%% <ul>
-%% <li>atoms other than true, false, null will be considered UTF-8
-%% strings (even as a proplist key)
-%% </li>
-%% <li>{json, IoList} will insert IoList directly into the output
-%% with no validation
-%% </li>
-%% <li>{array, Array} will be encoded as Array
-%% (legacy mochijson style)
-%% </li>
-%% <li>A non-empty raw proplist will be encoded as an object as long
-%% as the first pair does not have an atom key of json, struct,
-%% or array
-%% </li>
-%% </ul>
-
--module(mochijson2).
--author('bob@mochimedia.com').
--export([encoder/1, encode/1]).
--export([decoder/1, decode/1, decode/2]).
-
-%% This is a macro to placate syntax highlighters..
--define(Q, $\").
--define(ADV_COL(S, N), S#decoder{offset=N+S#decoder.offset,
- column=N+S#decoder.column}).
--define(INC_COL(S), S#decoder{offset=1+S#decoder.offset,
- column=1+S#decoder.column}).
--define(INC_LINE(S), S#decoder{offset=1+S#decoder.offset,
- column=1,
- line=1+S#decoder.line}).
--define(INC_CHAR(S, C),
- case C of
- $\n ->
- S#decoder{column=1,
- line=1+S#decoder.line,
- offset=1+S#decoder.offset};
- _ ->
- S#decoder{column=1+S#decoder.column,
- offset=1+S#decoder.offset}
- end).
--define(IS_WHITESPACE(C),
- (C =:= $\s orelse C =:= $\t orelse C =:= $\r orelse C =:= $\n)).
-
-%% @type json_string() = atom | binary()
-%% @type json_number() = integer() | float()
-%% @type json_array() = [json_term()]
-%% @type json_object() = {struct, [{json_string(), json_term()}]}
-%% @type json_eep18_object() = {[{json_string(), json_term()}]}
-%% @type json_iolist() = {json, iolist()}
-%% @type json_term() = json_string() | json_number() | json_array() |
-%% json_object() | json_eep18_object() | json_iolist()
-
--record(encoder, {handler=null,
- utf8=false}).
-
--record(decoder, {object_hook=null,
- offset=0,
- line=1,
- column=1,
- state=null}).
-
-%% @spec encoder([encoder_option()]) -> function()
-%% @doc Create an encoder/1 with the given options.
-%% @type encoder_option() = handler_option() | utf8_option()
-%% @type utf8_option() = boolean(). Emit unicode as utf8 (default - false)
-encoder(Options) ->
- State = parse_encoder_options(Options, #encoder{}),
- fun (O) -> json_encode(O, State) end.
-
-%% @spec encode(json_term()) -> iolist()
-%% @doc Encode the given as JSON to an iolist.
-encode(Any) ->
- json_encode(Any, #encoder{}).
-
-%% @spec decoder([decoder_option()]) -> function()
-%% @doc Create a decoder/1 with the given options.
-decoder(Options) ->
- State = parse_decoder_options(Options, #decoder{}),
- fun (O) -> json_decode(O, State) end.
-
-%% @spec decode(iolist(), [{format, proplist | eep18 | struct}]) -> json_term()
-%% @doc Decode the given iolist to Erlang terms using the given object format
-%% for decoding, where proplist returns JSON objects as [{binary(), json_term()}]
-%% proplists, eep18 returns JSON objects as {[binary(), json_term()]}, and struct
-%% returns them as-is.
-decode(S, Options) ->
- json_decode(S, parse_decoder_options(Options, #decoder{})).
-
-%% @spec decode(iolist()) -> json_term()
-%% @doc Decode the given iolist to Erlang terms.
-decode(S) ->
- json_decode(S, #decoder{}).
-
-%% Internal API
-
-parse_encoder_options([], State) ->
- State;
-parse_encoder_options([{handler, Handler} | Rest], State) ->
- parse_encoder_options(Rest, State#encoder{handler=Handler});
-parse_encoder_options([{utf8, Switch} | Rest], State) ->
- parse_encoder_options(Rest, State#encoder{utf8=Switch}).
-
-parse_decoder_options([], State) ->
- State;
-parse_decoder_options([{object_hook, Hook} | Rest], State) ->
- parse_decoder_options(Rest, State#decoder{object_hook=Hook});
-parse_decoder_options([{format, Format} | Rest], State)
- when Format =:= struct orelse Format =:= eep18 orelse Format =:= proplist ->
- parse_decoder_options(Rest, State#decoder{object_hook=Format}).
-
-json_encode(true, _State) ->
- <<"true">>;
-json_encode(false, _State) ->
- <<"false">>;
-json_encode(null, _State) ->
- <<"null">>;
-json_encode(I, _State) when is_integer(I) ->
- integer_to_list(I);
-json_encode(F, _State) when is_float(F) ->
- mochinum:digits(F);
-json_encode(S, State) when is_binary(S); is_atom(S) ->
- json_encode_string(S, State);
-json_encode([{K, _}|_] = Props, State) when (K =/= struct andalso
- K =/= array andalso
- K =/= json) ->
- json_encode_proplist(Props, State);
-json_encode({struct, Props}, State) when is_list(Props) ->
- json_encode_proplist(Props, State);
-json_encode({Props}, State) when is_list(Props) ->
- json_encode_proplist(Props, State);
-json_encode({}, State) ->
- json_encode_proplist([], State);
-json_encode(Array, State) when is_list(Array) ->
- json_encode_array(Array, State);
-json_encode({array, Array}, State) when is_list(Array) ->
- json_encode_array(Array, State);
-json_encode({json, IoList}, _State) ->
- IoList;
-json_encode(Bad, #encoder{handler=null}) ->
- exit({json_encode, {bad_term, Bad}});
-json_encode(Bad, State=#encoder{handler=Handler}) ->
- json_encode(Handler(Bad), State).
-
-json_encode_array([], _State) ->
- <<"[]">>;
-json_encode_array(L, State) ->
- F = fun (O, Acc) ->
- [$,, json_encode(O, State) | Acc]
- end,
- [$, | Acc1] = lists:foldl(F, "[", L),
- lists:reverse([$\] | Acc1]).
-
-json_encode_proplist([], _State) ->
- <<"{}">>;
-json_encode_proplist(Props, State) ->
- F = fun ({K, V}, Acc) ->
- KS = json_encode_string(K, State),
- VS = json_encode(V, State),
- [$,, VS, $:, KS | Acc]
- end,
- [$, | Acc1] = lists:foldl(F, "{", Props),
- lists:reverse([$\} | Acc1]).
-
-json_encode_string(A, State) when is_atom(A) ->
- L = atom_to_list(A),
- case json_string_is_safe(L) of
- true ->
- [?Q, L, ?Q];
- false ->
- json_encode_string_unicode(xmerl_ucs:from_utf8(L), State, [?Q])
- end;
-json_encode_string(B, State) when is_binary(B) ->
- case json_bin_is_safe(B) of
- true ->
- [?Q, B, ?Q];
- false ->
- json_encode_string_unicode(xmerl_ucs:from_utf8(B), State, [?Q])
- end;
-json_encode_string(I, _State) when is_integer(I) ->
- [?Q, integer_to_list(I), ?Q];
-json_encode_string(L, State) when is_list(L) ->
- case json_string_is_safe(L) of
- true ->
- [?Q, L, ?Q];
- false ->
- json_encode_string_unicode(L, State, [?Q])
- end.
-
-json_string_is_safe([]) ->
- true;
-json_string_is_safe([C | Rest]) ->
- case C of
- ?Q ->
- false;
- $\\ ->
- false;
- $\b ->
- false;
- $\f ->
- false;
- $\n ->
- false;
- $\r ->
- false;
- $\t ->
- false;
- C when C >= 0, C < $\s; C >= 16#7f, C =< 16#10FFFF ->
- false;
- C when C < 16#7f ->
- json_string_is_safe(Rest);
- _ ->
- false
- end.
-
-json_bin_is_safe(<<>>) ->
- true;
-json_bin_is_safe(<<C, Rest/binary>>) ->
- case C of
- ?Q ->
- false;
- $\\ ->
- false;
- $\b ->
- false;
- $\f ->
- false;
- $\n ->
- false;
- $\r ->
- false;
- $\t ->
- false;
- C when C >= 0, C < $\s; C >= 16#7f ->
- false;
- C when C < 16#7f ->
- json_bin_is_safe(Rest)
- end.
-
-json_encode_string_unicode([], _State, Acc) ->
- lists:reverse([$\" | Acc]);
-json_encode_string_unicode([C | Cs], State, Acc) ->
- Acc1 = case C of
- ?Q ->
- [?Q, $\\ | Acc];
- %% Escaping solidus is only useful when trying to protect
- %% against "</script>" injection attacks which are only
- %% possible when JSON is inserted into a HTML document
- %% in-line. mochijson2 does not protect you from this, so
- %% if you do insert directly into HTML then you need to
- %% uncomment the following case or escape the output of encode.
- %%
- %% $/ ->
- %% [$/, $\\ | Acc];
- %%
- $\\ ->
- [$\\, $\\ | Acc];
- $\b ->
- [$b, $\\ | Acc];
- $\f ->
- [$f, $\\ | Acc];
- $\n ->
- [$n, $\\ | Acc];
- $\r ->
- [$r, $\\ | Acc];
- $\t ->
- [$t, $\\ | Acc];
- C when C >= 0, C < $\s ->
- [unihex(C) | Acc];
- C when C >= 16#7f, C =< 16#10FFFF, State#encoder.utf8 ->
- [xmerl_ucs:to_utf8(C) | Acc];
- C when C >= 16#7f, C =< 16#10FFFF, not State#encoder.utf8 ->
- [unihex(C) | Acc];
- C when C < 16#7f ->
- [C | Acc];
- _ ->
- exit({json_encode, {bad_char, C}})
- end,
- json_encode_string_unicode(Cs, State, Acc1).
-
-hexdigit(C) when C >= 0, C =< 9 ->
- C + $0;
-hexdigit(C) when C =< 15 ->
- C + $a - 10.
-
-unihex(C) when C < 16#10000 ->
- <<D3:4, D2:4, D1:4, D0:4>> = <<C:16>>,
- Digits = [hexdigit(D) || D <- [D3, D2, D1, D0]],
- [$\\, $u | Digits];
-unihex(C) when C =< 16#10FFFF ->
- N = C - 16#10000,
- S1 = 16#d800 bor ((N bsr 10) band 16#3ff),
- S2 = 16#dc00 bor (N band 16#3ff),
- [unihex(S1), unihex(S2)].
-
-json_decode(L, S) when is_list(L) ->
- json_decode(iolist_to_binary(L), S);
-json_decode(B, S) ->
- {Res, S1} = decode1(B, S),
- {eof, _} = tokenize(B, S1#decoder{state=trim}),
- Res.
-
-decode1(B, S=#decoder{state=null}) ->
- case tokenize(B, S#decoder{state=any}) of
- {{const, C}, S1} ->
- {C, S1};
- {start_array, S1} ->
- decode_array(B, S1);
- {start_object, S1} ->
- decode_object(B, S1)
- end.
-
-make_object(V, #decoder{object_hook=N}) when N =:= null orelse N =:= struct ->
- V;
-make_object({struct, P}, #decoder{object_hook=eep18}) ->
- {P};
-make_object({struct, P}, #decoder{object_hook=proplist}) ->
- P;
-make_object(V, #decoder{object_hook=Hook}) ->
- Hook(V).
-
-decode_object(B, S) ->
- decode_object(B, S#decoder{state=key}, []).
-
-decode_object(B, S=#decoder{state=key}, Acc) ->
- case tokenize(B, S) of
- {end_object, S1} ->
- V = make_object({struct, lists:reverse(Acc)}, S1),
- {V, S1#decoder{state=null}};
- {{const, K}, S1} ->
- {colon, S2} = tokenize(B, S1),
- {V, S3} = decode1(B, S2#decoder{state=null}),
- decode_object(B, S3#decoder{state=comma}, [{K, V} | Acc])
- end;
-decode_object(B, S=#decoder{state=comma}, Acc) ->
- case tokenize(B, S) of
- {end_object, S1} ->
- V = make_object({struct, lists:reverse(Acc)}, S1),
- {V, S1#decoder{state=null}};
- {comma, S1} ->
- decode_object(B, S1#decoder{state=key}, Acc)
- end.
-
-decode_array(B, S) ->
- decode_array(B, S#decoder{state=any}, []).
-
-decode_array(B, S=#decoder{state=any}, Acc) ->
- case tokenize(B, S) of
- {end_array, S1} ->
- {lists:reverse(Acc), S1#decoder{state=null}};
- {start_array, S1} ->
- {Array, S2} = decode_array(B, S1),
- decode_array(B, S2#decoder{state=comma}, [Array | Acc]);
- {start_object, S1} ->
- {Array, S2} = decode_object(B, S1),
- decode_array(B, S2#decoder{state=comma}, [Array | Acc]);
- {{const, Const}, S1} ->
- decode_array(B, S1#decoder{state=comma}, [Const | Acc])
- end;
-decode_array(B, S=#decoder{state=comma}, Acc) ->
- case tokenize(B, S) of
- {end_array, S1} ->
- {lists:reverse(Acc), S1#decoder{state=null}};
- {comma, S1} ->
- decode_array(B, S1#decoder{state=any}, Acc)
- end.
-
-tokenize_string(B, S=#decoder{offset=O}) ->
- case tokenize_string_fast(B, O) of
- {escape, O1} ->
- Length = O1 - O,
- S1 = ?ADV_COL(S, Length),
- <<_:O/binary, Head:Length/binary, _/binary>> = B,
- tokenize_string(B, S1, lists:reverse(binary_to_list(Head)));
- O1 ->
- Length = O1 - O,
- <<_:O/binary, String:Length/binary, ?Q, _/binary>> = B,
- {{const, String}, ?ADV_COL(S, Length + 1)}
- end.
-
-tokenize_string_fast(B, O) ->
- case B of
- <<_:O/binary, ?Q, _/binary>> ->
- O;
- <<_:O/binary, $\\, _/binary>> ->
- {escape, O};
- <<_:O/binary, C1, _/binary>> when C1 < 128 ->
- tokenize_string_fast(B, 1 + O);
- <<_:O/binary, C1, C2, _/binary>> when C1 >= 194, C1 =< 223,
- C2 >= 128, C2 =< 191 ->
- tokenize_string_fast(B, 2 + O);
- <<_:O/binary, C1, C2, C3, _/binary>> when C1 >= 224, C1 =< 239,
- C2 >= 128, C2 =< 191,
- C3 >= 128, C3 =< 191 ->
- tokenize_string_fast(B, 3 + O);
- <<_:O/binary, C1, C2, C3, C4, _/binary>> when C1 >= 240, C1 =< 244,
- C2 >= 128, C2 =< 191,
- C3 >= 128, C3 =< 191,
- C4 >= 128, C4 =< 191 ->
- tokenize_string_fast(B, 4 + O);
- _ ->
- throw(invalid_utf8)
- end.
-
-tokenize_string(B, S=#decoder{offset=O}, Acc) ->
- case B of
- <<_:O/binary, ?Q, _/binary>> ->
- {{const, iolist_to_binary(lists:reverse(Acc))}, ?INC_COL(S)};
- <<_:O/binary, "\\\"", _/binary>> ->
- tokenize_string(B, ?ADV_COL(S, 2), [$\" | Acc]);
- <<_:O/binary, "\\\\", _/binary>> ->
- tokenize_string(B, ?ADV_COL(S, 2), [$\\ | Acc]);
- <<_:O/binary, "\\/", _/binary>> ->
- tokenize_string(B, ?ADV_COL(S, 2), [$/ | Acc]);
- <<_:O/binary, "\\b", _/binary>> ->
- tokenize_string(B, ?ADV_COL(S, 2), [$\b | Acc]);
- <<_:O/binary, "\\f", _/binary>> ->
- tokenize_string(B, ?ADV_COL(S, 2), [$\f | Acc]);
- <<_:O/binary, "\\n", _/binary>> ->
- tokenize_string(B, ?ADV_COL(S, 2), [$\n | Acc]);
- <<_:O/binary, "\\r", _/binary>> ->
- tokenize_string(B, ?ADV_COL(S, 2), [$\r | Acc]);
- <<_:O/binary, "\\t", _/binary>> ->
- tokenize_string(B, ?ADV_COL(S, 2), [$\t | Acc]);
- <<_:O/binary, "\\u", C3, C2, C1, C0, Rest/binary>> ->
- C = erlang:list_to_integer([C3, C2, C1, C0], 16),
- if C > 16#D7FF, C < 16#DC00 ->
- %% coalesce UTF-16 surrogate pair
- <<"\\u", D3, D2, D1, D0, _/binary>> = Rest,
- D = erlang:list_to_integer([D3,D2,D1,D0], 16),
- [CodePoint] = xmerl_ucs:from_utf16be(<<C:16/big-unsigned-integer,
- D:16/big-unsigned-integer>>),
- Acc1 = lists:reverse(xmerl_ucs:to_utf8(CodePoint), Acc),
- tokenize_string(B, ?ADV_COL(S, 12), Acc1);
- true ->
- Acc1 = lists:reverse(xmerl_ucs:to_utf8(C), Acc),
- tokenize_string(B, ?ADV_COL(S, 6), Acc1)
- end;
- <<_:O/binary, C1, _/binary>> when C1 < 128 ->
- tokenize_string(B, ?INC_CHAR(S, C1), [C1 | Acc]);
- <<_:O/binary, C1, C2, _/binary>> when C1 >= 194, C1 =< 223,
- C2 >= 128, C2 =< 191 ->
- tokenize_string(B, ?ADV_COL(S, 2), [C2, C1 | Acc]);
- <<_:O/binary, C1, C2, C3, _/binary>> when C1 >= 224, C1 =< 239,
- C2 >= 128, C2 =< 191,
- C3 >= 128, C3 =< 191 ->
- tokenize_string(B, ?ADV_COL(S, 3), [C3, C2, C1 | Acc]);
- <<_:O/binary, C1, C2, C3, C4, _/binary>> when C1 >= 240, C1 =< 244,
- C2 >= 128, C2 =< 191,
- C3 >= 128, C3 =< 191,
- C4 >= 128, C4 =< 191 ->
- tokenize_string(B, ?ADV_COL(S, 4), [C4, C3, C2, C1 | Acc]);
- _ ->
- throw(invalid_utf8)
- end.
-
-tokenize_number(B, S) ->
- case tokenize_number(B, sign, S, []) of
- {{int, Int}, S1} ->
- {{const, list_to_integer(Int)}, S1};
- {{float, Float}, S1} ->
- {{const, list_to_float(Float)}, S1}
- end.
-
-tokenize_number(B, sign, S=#decoder{offset=O}, []) ->
- case B of
- <<_:O/binary, $-, _/binary>> ->
- tokenize_number(B, int, ?INC_COL(S), [$-]);
- _ ->
- tokenize_number(B, int, S, [])
- end;
-tokenize_number(B, int, S=#decoder{offset=O}, Acc) ->
- case B of
- <<_:O/binary, $0, _/binary>> ->
- tokenize_number(B, frac, ?INC_COL(S), [$0 | Acc]);
- <<_:O/binary, C, _/binary>> when C >= $1 andalso C =< $9 ->
- tokenize_number(B, int1, ?INC_COL(S), [C | Acc])
- end;
-tokenize_number(B, int1, S=#decoder{offset=O}, Acc) ->
- case B of
- <<_:O/binary, C, _/binary>> when C >= $0 andalso C =< $9 ->
- tokenize_number(B, int1, ?INC_COL(S), [C | Acc]);
- _ ->
- tokenize_number(B, frac, S, Acc)
- end;
-tokenize_number(B, frac, S=#decoder{offset=O}, Acc) ->
- case B of
- <<_:O/binary, $., C, _/binary>> when C >= $0, C =< $9 ->
- tokenize_number(B, frac1, ?ADV_COL(S, 2), [C, $. | Acc]);
- <<_:O/binary, E, _/binary>> when E =:= $e orelse E =:= $E ->
- tokenize_number(B, esign, ?INC_COL(S), [$e, $0, $. | Acc]);
- _ ->
- {{int, lists:reverse(Acc)}, S}
- end;
-tokenize_number(B, frac1, S=#decoder{offset=O}, Acc) ->
- case B of
- <<_:O/binary, C, _/binary>> when C >= $0 andalso C =< $9 ->
- tokenize_number(B, frac1, ?INC_COL(S), [C | Acc]);
- <<_:O/binary, E, _/binary>> when E =:= $e orelse E =:= $E ->
- tokenize_number(B, esign, ?INC_COL(S), [$e | Acc]);
- _ ->
- {{float, lists:reverse(Acc)}, S}
- end;
-tokenize_number(B, esign, S=#decoder{offset=O}, Acc) ->
- case B of
- <<_:O/binary, C, _/binary>> when C =:= $- orelse C=:= $+ ->
- tokenize_number(B, eint, ?INC_COL(S), [C | Acc]);
- _ ->
- tokenize_number(B, eint, S, Acc)
- end;
-tokenize_number(B, eint, S=#decoder{offset=O}, Acc) ->
- case B of
- <<_:O/binary, C, _/binary>> when C >= $0 andalso C =< $9 ->
- tokenize_number(B, eint1, ?INC_COL(S), [C | Acc])
- end;
-tokenize_number(B, eint1, S=#decoder{offset=O}, Acc) ->
- case B of
- <<_:O/binary, C, _/binary>> when C >= $0 andalso C =< $9 ->
- tokenize_number(B, eint1, ?INC_COL(S), [C | Acc]);
- _ ->
- {{float, lists:reverse(Acc)}, S}
- end.
-
-tokenize(B, S=#decoder{offset=O}) ->
- case B of
- <<_:O/binary, C, _/binary>> when ?IS_WHITESPACE(C) ->
- tokenize(B, ?INC_CHAR(S, C));
- <<_:O/binary, "{", _/binary>> ->
- {start_object, ?INC_COL(S)};
- <<_:O/binary, "}", _/binary>> ->
- {end_object, ?INC_COL(S)};
- <<_:O/binary, "[", _/binary>> ->
- {start_array, ?INC_COL(S)};
- <<_:O/binary, "]", _/binary>> ->
- {end_array, ?INC_COL(S)};
- <<_:O/binary, ",", _/binary>> ->
- {comma, ?INC_COL(S)};
- <<_:O/binary, ":", _/binary>> ->
- {colon, ?INC_COL(S)};
- <<_:O/binary, "null", _/binary>> ->
- {{const, null}, ?ADV_COL(S, 4)};
- <<_:O/binary, "true", _/binary>> ->
- {{const, true}, ?ADV_COL(S, 4)};
- <<_:O/binary, "false", _/binary>> ->
- {{const, false}, ?ADV_COL(S, 5)};
- <<_:O/binary, "\"", _/binary>> ->
- tokenize_string(B, ?INC_COL(S));
- <<_:O/binary, C, _/binary>> when (C >= $0 andalso C =< $9)
- orelse C =:= $- ->
- tokenize_number(B, S);
- <<_:O/binary>> ->
- trim = S#decoder.state,
- {eof, S}
- end.
-%%
-%% Tests
-%%
--ifdef(TEST).
--include_lib("eunit/include/eunit.hrl").
-
-
-%% testing constructs borrowed from the Yaws JSON implementation.
-
-%% Create an object from a list of Key/Value pairs.
-
-obj_new() ->
- {struct, []}.
-
-is_obj({struct, Props}) ->
- F = fun ({K, _}) when is_binary(K) -> true end,
- lists:all(F, Props).
-
-obj_from_list(Props) ->
- Obj = {struct, Props},
- ?assert(is_obj(Obj)),
- Obj.
-
-%% Test for equivalence of Erlang terms.
-%% Due to arbitrary order of construction, equivalent objects might
-%% compare unequal as erlang terms, so we need to carefully recurse
-%% through aggregates (tuples and objects).
-
-equiv({struct, Props1}, {struct, Props2}) ->
- equiv_object(Props1, Props2);
-equiv(L1, L2) when is_list(L1), is_list(L2) ->
- equiv_list(L1, L2);
-equiv(N1, N2) when is_number(N1), is_number(N2) -> N1 == N2;
-equiv(B1, B2) when is_binary(B1), is_binary(B2) -> B1 == B2;
-equiv(A, A) when A =:= true orelse A =:= false orelse A =:= null -> true.
-
-%% Object representation and traversal order is unknown.
-%% Use the sledgehammer and sort property lists.
-
-equiv_object(Props1, Props2) ->
- L1 = lists:keysort(1, Props1),
- L2 = lists:keysort(1, Props2),
- Pairs = lists:zip(L1, L2),
- true = lists:all(fun({{K1, V1}, {K2, V2}}) ->
- equiv(K1, K2) and equiv(V1, V2)
- end, Pairs).
-
-%% Recursively compare tuple elements for equivalence.
-
-equiv_list([], []) ->
- true;
-equiv_list([V1 | L1], [V2 | L2]) ->
- equiv(V1, V2) andalso equiv_list(L1, L2).
-
-decode_test() ->
- [1199344435545.0, 1] = decode(<<"[1199344435545.0,1]">>),
- <<16#F0,16#9D,16#9C,16#95>> = decode([34,"\\ud835","\\udf15",34]).
-
-e2j_vec_test() ->
- test_one(e2j_test_vec(utf8), 1).
-
-test_one([], _N) ->
- %% io:format("~p tests passed~n", [N-1]),
- ok;
-test_one([{E, J} | Rest], N) ->
- %% io:format("[~p] ~p ~p~n", [N, E, J]),
- true = equiv(E, decode(J)),
- true = equiv(E, decode(encode(E))),
- test_one(Rest, 1+N).
-
-e2j_test_vec(utf8) ->
- [
- {1, "1"},
- {3.1416, "3.14160"}, %% text representation may truncate, trail zeroes
- {-1, "-1"},
- {-3.1416, "-3.14160"},
- {12.0e10, "1.20000e+11"},
- {1.234E+10, "1.23400e+10"},
- {-1.234E-10, "-1.23400e-10"},
- {10.0, "1.0e+01"},
- {123.456, "1.23456E+2"},
- {10.0, "1e1"},
- {<<"foo">>, "\"foo\""},
- {<<"foo", 5, "bar">>, "\"foo\\u0005bar\""},
- {<<"">>, "\"\""},
- {<<"\n\n\n">>, "\"\\n\\n\\n\""},
- {<<"\" \b\f\r\n\t\"">>, "\"\\\" \\b\\f\\r\\n\\t\\\"\""},
- {obj_new(), "{}"},
- {obj_from_list([{<<"foo">>, <<"bar">>}]), "{\"foo\":\"bar\"}"},
- {obj_from_list([{<<"foo">>, <<"bar">>}, {<<"baz">>, 123}]),
- "{\"foo\":\"bar\",\"baz\":123}"},
- {[], "[]"},
- {[[]], "[[]]"},
- {[1, <<"foo">>], "[1,\"foo\"]"},
-
- %% json array in a json object
- {obj_from_list([{<<"foo">>, [123]}]),
- "{\"foo\":[123]}"},
-
- %% json object in a json object
- {obj_from_list([{<<"foo">>, obj_from_list([{<<"bar">>, true}])}]),
- "{\"foo\":{\"bar\":true}}"},
-
- %% fold evaluation order
- {obj_from_list([{<<"foo">>, []},
- {<<"bar">>, obj_from_list([{<<"baz">>, true}])},
- {<<"alice">>, <<"bob">>}]),
- "{\"foo\":[],\"bar\":{\"baz\":true},\"alice\":\"bob\"}"},
-
- %% json object in a json array
- {[-123, <<"foo">>, obj_from_list([{<<"bar">>, []}]), null],
- "[-123,\"foo\",{\"bar\":[]},null]"}
- ].
-
-%% test utf8 encoding
-encoder_utf8_test() ->
- %% safe conversion case (default)
- [34,"\\u0001","\\u0442","\\u0435","\\u0441","\\u0442",34] =
- encode(<<1,"\321\202\320\265\321\201\321\202">>),
-
- %% raw utf8 output (optional)
- Enc = mochijson2:encoder([{utf8, true}]),
- [34,"\\u0001",[209,130],[208,181],[209,129],[209,130],34] =
- Enc(<<1,"\321\202\320\265\321\201\321\202">>).
-
-input_validation_test() ->
- Good = [
- {16#00A3, <<?Q, 16#C2, 16#A3, ?Q>>}, %% pound
- {16#20AC, <<?Q, 16#E2, 16#82, 16#AC, ?Q>>}, %% euro
- {16#10196, <<?Q, 16#F0, 16#90, 16#86, 16#96, ?Q>>} %% denarius
- ],
- lists:foreach(fun({CodePoint, UTF8}) ->
- Expect = list_to_binary(xmerl_ucs:to_utf8(CodePoint)),
- Expect = decode(UTF8)
- end, Good),
-
- Bad = [
- %% 2nd, 3rd, or 4th byte of a multi-byte sequence w/o leading byte
- <<?Q, 16#80, ?Q>>,
- %% missing continuations, last byte in each should be 80-BF
- <<?Q, 16#C2, 16#7F, ?Q>>,
- <<?Q, 16#E0, 16#80,16#7F, ?Q>>,
- <<?Q, 16#F0, 16#80, 16#80, 16#7F, ?Q>>,
- %% we don't support code points > 10FFFF per RFC 3629
- <<?Q, 16#F5, 16#80, 16#80, 16#80, ?Q>>,
- %% escape characters trigger a different code path
- <<?Q, $\\, $\n, 16#80, ?Q>>
- ],
- lists:foreach(
- fun(X) ->
- ok = try decode(X) catch invalid_utf8 -> ok end,
- %% could be {ucs,{bad_utf8_character_code}} or
- %% {json_encode,{bad_char,_}}
- {'EXIT', _} = (catch encode(X))
- end, Bad).
-
-inline_json_test() ->
- ?assertEqual(<<"\"iodata iodata\"">>,
- iolist_to_binary(
- encode({json, [<<"\"iodata">>, " iodata\""]}))),
- ?assertEqual({struct, [{<<"key">>, <<"iodata iodata">>}]},
- decode(
- encode({struct,
- [{key, {json, [<<"\"iodata">>, " iodata\""]}}]}))),
- ok.
-
-big_unicode_test() ->
- UTF8Seq = list_to_binary(xmerl_ucs:to_utf8(16#0001d120)),
- ?assertEqual(
- <<"\"\\ud834\\udd20\"">>,
- iolist_to_binary(encode(UTF8Seq))),
- ?assertEqual(
- UTF8Seq,
- decode(iolist_to_binary(encode(UTF8Seq)))),
- ok.
-
-custom_decoder_test() ->
- ?assertEqual(
- {struct, [{<<"key">>, <<"value">>}]},
- (decoder([]))("{\"key\": \"value\"}")),
- F = fun ({struct, [{<<"key">>, <<"value">>}]}) -> win end,
- ?assertEqual(
- win,
- (decoder([{object_hook, F}]))("{\"key\": \"value\"}")),
- ok.
-
-atom_test() ->
- %% JSON native atoms
- [begin
- ?assertEqual(A, decode(atom_to_list(A))),
- ?assertEqual(iolist_to_binary(atom_to_list(A)),
- iolist_to_binary(encode(A)))
- end || A <- [true, false, null]],
- %% Atom to string
- ?assertEqual(
- <<"\"foo\"">>,
- iolist_to_binary(encode(foo))),
- ?assertEqual(
- <<"\"\\ud834\\udd20\"">>,
- iolist_to_binary(encode(list_to_atom(xmerl_ucs:to_utf8(16#0001d120))))),
- ok.
-
-key_encode_test() ->
- %% Some forms are accepted as keys that would not be strings in other
- %% cases
- ?assertEqual(
- <<"{\"foo\":1}">>,
- iolist_to_binary(encode({struct, [{foo, 1}]}))),
- ?assertEqual(
- <<"{\"foo\":1}">>,
- iolist_to_binary(encode({struct, [{<<"foo">>, 1}]}))),
- ?assertEqual(
- <<"{\"foo\":1}">>,
- iolist_to_binary(encode({struct, [{"foo", 1}]}))),
- ?assertEqual(
- <<"{\"foo\":1}">>,
- iolist_to_binary(encode([{foo, 1}]))),
- ?assertEqual(
- <<"{\"foo\":1}">>,
- iolist_to_binary(encode([{<<"foo">>, 1}]))),
- ?assertEqual(
- <<"{\"foo\":1}">>,
- iolist_to_binary(encode([{"foo", 1}]))),
- ?assertEqual(
- <<"{\"\\ud834\\udd20\":1}">>,
- iolist_to_binary(
- encode({struct, [{[16#0001d120], 1}]}))),
- ?assertEqual(
- <<"{\"1\":1}">>,
- iolist_to_binary(encode({struct, [{1, 1}]}))),
- ok.
-
-unsafe_chars_test() ->
- Chars = "\"\\\b\f\n\r\t",
- [begin
- ?assertEqual(false, json_string_is_safe([C])),
- ?assertEqual(false, json_bin_is_safe(<<C>>)),
- ?assertEqual(<<C>>, decode(encode(<<C>>)))
- end || C <- Chars],
- ?assertEqual(
- false,
- json_string_is_safe([16#0001d120])),
- ?assertEqual(
- false,
- json_bin_is_safe(list_to_binary(xmerl_ucs:to_utf8(16#0001d120)))),
- ?assertEqual(
- [16#0001d120],
- xmerl_ucs:from_utf8(
- binary_to_list(
- decode(encode(list_to_atom(xmerl_ucs:to_utf8(16#0001d120))))))),
- ?assertEqual(
- false,
- json_string_is_safe([16#110000])),
- ?assertEqual(
- false,
- json_bin_is_safe(list_to_binary(xmerl_ucs:to_utf8([16#110000])))),
- %% solidus can be escaped but isn't unsafe by default
- ?assertEqual(
- <<"/">>,
- decode(<<"\"\\/\"">>)),
- ok.
-
-int_test() ->
- ?assertEqual(0, decode("0")),
- ?assertEqual(1, decode("1")),
- ?assertEqual(11, decode("11")),
- ok.
-
-large_int_test() ->
- ?assertEqual(<<"-2147483649214748364921474836492147483649">>,
- iolist_to_binary(encode(-2147483649214748364921474836492147483649))),
- ?assertEqual(<<"2147483649214748364921474836492147483649">>,
- iolist_to_binary(encode(2147483649214748364921474836492147483649))),
- ok.
-
-float_test() ->
- ?assertEqual(<<"-2147483649.0">>, iolist_to_binary(encode(-2147483649.0))),
- ?assertEqual(<<"2147483648.0">>, iolist_to_binary(encode(2147483648.0))),
- ok.
-
-handler_test() ->
- ?assertEqual(
- {'EXIT',{json_encode,{bad_term,{x,y}}}},
- catch encode({x,y})),
- F = fun ({x,y}) -> [] end,
- ?assertEqual(
- <<"[]">>,
- iolist_to_binary((encoder([{handler, F}]))({x, y}))),
- ok.
-
-encode_empty_test_() ->
- [{A, ?_assertEqual(<<"{}">>, iolist_to_binary(encode(B)))}
- || {A, B} <- [{"eep18 {}", {}},
- {"eep18 {[]}", {[]}},
- {"{struct, []}", {struct, []}}]].
-
-encode_test_() ->
- P = [{<<"k">>, <<"v">>}],
- JSON = iolist_to_binary(encode({struct, P})),
- [{atom_to_list(F),
- ?_assertEqual(JSON, iolist_to_binary(encode(decode(JSON, [{format, F}]))))}
- || F <- [struct, eep18, proplist]].
-
-format_test_() ->
- P = [{<<"k">>, <<"v">>}],
- JSON = iolist_to_binary(encode({struct, P})),
- [{atom_to_list(F),
- ?_assertEqual(A, decode(JSON, [{format, F}]))}
- || {F, A} <- [{struct, {struct, P}},
- {eep18, {P}},
- {proplist, P}]].
-
--endif.
diff --git a/src/pmon.erl b/src/pmon.erl
deleted file mode 100644
index f42530022a..0000000000
--- a/src/pmon.erl
+++ /dev/null
@@ -1,109 +0,0 @@
-%% The contents of this file are subject to the Mozilla Public License
-%% Version 1.1 (the "License"); you may not use this file except in
-%% compliance with the License. You may obtain a copy of the License
-%% at http://www.mozilla.org/MPL/
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and
-%% limitations under the License.
-%%
-%% The Original Code is RabbitMQ.
-%%
-%% The Initial Developer of the Original Code is GoPivotal, Inc.
-%% Copyright (c) 2011-2015 Pivotal Software, Inc. All rights reserved.
-%%
-
--module(pmon).
-
-%% Process Monitor
-%% ================
-%%
-%% This module monitors processes so that every process has at most
-%% 1 monitor.
-%% Processes monitored can be dynamically added and removed.
-%%
-%% Unlike erlang:[de]monitor* functions, this module
-%% provides basic querying capability and avoids contacting down nodes.
-%%
-%% It is used to monitor nodes, queue mirrors, and by
-%% the queue collector, among other things.
-
--export([new/0, new/1, monitor/2, monitor_all/2, demonitor/2,
- is_monitored/2, erase/2, monitored/1, is_empty/1]).
-
--compile({no_auto_import, [monitor/2]}).
-
--record(state, {dict, module}).
-
--ifdef(use_specs).
-
-%%----------------------------------------------------------------------------
-
--export_type([?MODULE/0]).
-
--opaque(?MODULE() :: #state{dict :: dict:dict(),
- module :: atom()}).
-
--type(item() :: pid() | {atom(), node()}).
-
--spec(new/0 :: () -> ?MODULE()).
--spec(new/1 :: ('erlang' | 'delegate') -> ?MODULE()).
--spec(monitor/2 :: (item(), ?MODULE()) -> ?MODULE()).
--spec(monitor_all/2 :: ([item()], ?MODULE()) -> ?MODULE()).
--spec(demonitor/2 :: (item(), ?MODULE()) -> ?MODULE()).
--spec(is_monitored/2 :: (item(), ?MODULE()) -> boolean()).
--spec(erase/2 :: (item(), ?MODULE()) -> ?MODULE()).
--spec(monitored/1 :: (?MODULE()) -> [item()]).
--spec(is_empty/1 :: (?MODULE()) -> boolean()).
-
--endif.
-
-new() -> new(erlang).
-
-new(Module) -> #state{dict = dict:new(),
- module = Module}.
-
-monitor(Item, S = #state{dict = M, module = Module}) ->
- case dict:is_key(Item, M) of
- true -> S;
- false -> case node_alive_shortcut(Item) of
- true -> Ref = Module:monitor(process, Item),
- S#state{dict = dict:store(Item, Ref, M)};
- false -> self() ! {'DOWN', fake_ref, process, Item,
- nodedown},
- S
- end
- end.
-
-monitor_all([], S) -> S; %% optimisation
-monitor_all([Item], S) -> monitor(Item, S); %% optimisation
-monitor_all(Items, S) -> lists:foldl(fun monitor/2, S, Items).
-
-demonitor(Item, S = #state{dict = M, module = Module}) ->
- case dict:find(Item, M) of
- {ok, MRef} -> Module:demonitor(MRef),
- S#state{dict = dict:erase(Item, M)};
- error -> M
- end.
-
-is_monitored(Item, #state{dict = M}) -> dict:is_key(Item, M).
-
-erase(Item, S = #state{dict = M}) -> S#state{dict = dict:erase(Item, M)}.
-
-monitored(#state{dict = M}) -> dict:fetch_keys(M).
-
-is_empty(#state{dict = M}) -> dict:size(M) == 0.
-
-%%----------------------------------------------------------------------------
-
-%% We check here to see if the node is alive in order to avoid trying
-%% to connect to it if it isn't - this can cause substantial
-%% slowdowns. We can't perform this shortcut if passed {Name, Node}
-%% since we would need to convert that into a pid for the fake 'DOWN'
-%% message, so we always return true here - but that's OK, it's just
-%% an optimisation.
-node_alive_shortcut(P) when is_pid(P) ->
- lists:member(node(P), [node() | nodes()]);
-node_alive_shortcut({_Name, _Node}) ->
- true.
diff --git a/src/priority_queue.erl b/src/priority_queue.erl
deleted file mode 100644
index 88c69513d7..0000000000
--- a/src/priority_queue.erl
+++ /dev/null
@@ -1,227 +0,0 @@
-%% The contents of this file are subject to the Mozilla Public License
-%% Version 1.1 (the "License"); you may not use this file except in
-%% compliance with the License. You may obtain a copy of the License
-%% at http://www.mozilla.org/MPL/
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and
-%% limitations under the License.
-%%
-%% The Original Code is RabbitMQ.
-%%
-%% The Initial Developer of the Original Code is GoPivotal, Inc.
-%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved.
-%%
-
-%% Priority queues have essentially the same interface as ordinary
-%% queues, except that a) there is an in/3 that takes a priority, and
-%% b) we have only implemented the core API we need.
-%%
-%% Priorities should be integers - the higher the value the higher the
-%% priority - but we don't actually check that.
-%%
-%% in/2 inserts items with priority 0.
-%%
-%% We optimise the case where a priority queue is being used just like
-%% an ordinary queue. When that is the case we represent the priority
-%% queue as an ordinary queue. We could just call into the 'queue'
-%% module for that, but for efficiency we implement the relevant
-%% functions directly in here, thus saving on inter-module calls and
-%% eliminating a level of boxing.
-%%
-%% When the queue contains items with non-zero priorities, it is
-%% represented as a sorted kv list with the inverted Priority as the
-%% key and an ordinary queue as the value. Here again we use our own
-%% ordinary queue implemention for efficiency, often making recursive
-%% calls into the same function knowing that ordinary queues represent
-%% a base case.
-
-
--module(priority_queue).
-
--export([new/0, is_queue/1, is_empty/1, len/1, to_list/1, from_list/1,
- in/2, in/3, out/1, out_p/1, join/2, filter/2, fold/3, highest/1]).
-
-%%----------------------------------------------------------------------------
-
--ifdef(use_specs).
-
--export_type([q/0]).
-
--type(q() :: pqueue()).
--type(priority() :: integer() | 'infinity').
--type(squeue() :: {queue, [any()], [any()], non_neg_integer()}).
--type(pqueue() :: squeue() | {pqueue, [{priority(), squeue()}]}).
-
--spec(new/0 :: () -> pqueue()).
--spec(is_queue/1 :: (any()) -> boolean()).
--spec(is_empty/1 :: (pqueue()) -> boolean()).
--spec(len/1 :: (pqueue()) -> non_neg_integer()).
--spec(to_list/1 :: (pqueue()) -> [{priority(), any()}]).
--spec(from_list/1 :: ([{priority(), any()}]) -> pqueue()).
--spec(in/2 :: (any(), pqueue()) -> pqueue()).
--spec(in/3 :: (any(), priority(), pqueue()) -> pqueue()).
--spec(out/1 :: (pqueue()) -> {empty | {value, any()}, pqueue()}).
--spec(out_p/1 :: (pqueue()) -> {empty | {value, any(), priority()}, pqueue()}).
--spec(join/2 :: (pqueue(), pqueue()) -> pqueue()).
--spec(filter/2 :: (fun ((any()) -> boolean()), pqueue()) -> pqueue()).
--spec(fold/3 ::
- (fun ((any(), priority(), A) -> A), A, pqueue()) -> A).
--spec(highest/1 :: (pqueue()) -> priority() | 'empty').
-
--endif.
-
-%%----------------------------------------------------------------------------
-
-new() ->
- {queue, [], [], 0}.
-
-is_queue({queue, R, F, L}) when is_list(R), is_list(F), is_integer(L) ->
- true;
-is_queue({pqueue, Queues}) when is_list(Queues) ->
- lists:all(fun ({infinity, Q}) -> is_queue(Q);
- ({P, Q}) -> is_integer(P) andalso is_queue(Q)
- end, Queues);
-is_queue(_) ->
- false.
-
-is_empty({queue, [], [], 0}) ->
- true;
-is_empty(_) ->
- false.
-
-len({queue, _R, _F, L}) ->
- L;
-len({pqueue, Queues}) ->
- lists:sum([len(Q) || {_, Q} <- Queues]).
-
-to_list({queue, In, Out, _Len}) when is_list(In), is_list(Out) ->
- [{0, V} || V <- Out ++ lists:reverse(In, [])];
-to_list({pqueue, Queues}) ->
- [{maybe_negate_priority(P), V} || {P, Q} <- Queues,
- {0, V} <- to_list(Q)].
-
-from_list(L) ->
- lists:foldl(fun ({P, E}, Q) -> in(E, P, Q) end, new(), L).
-
-in(Item, Q) ->
- in(Item, 0, Q).
-
-in(X, 0, {queue, [_] = In, [], 1}) ->
- {queue, [X], In, 2};
-in(X, 0, {queue, In, Out, Len}) when is_list(In), is_list(Out) ->
- {queue, [X|In], Out, Len + 1};
-in(X, Priority, _Q = {queue, [], [], 0}) ->
- in(X, Priority, {pqueue, []});
-in(X, Priority, Q = {queue, _, _, _}) ->
- in(X, Priority, {pqueue, [{0, Q}]});
-in(X, Priority, {pqueue, Queues}) ->
- P = maybe_negate_priority(Priority),
- {pqueue, case lists:keysearch(P, 1, Queues) of
- {value, {_, Q}} ->
- lists:keyreplace(P, 1, Queues, {P, in(X, Q)});
- false when P == infinity ->
- [{P, {queue, [X], [], 1}} | Queues];
- false ->
- case Queues of
- [{infinity, InfQueue} | Queues1] ->
- [{infinity, InfQueue} |
- lists:keysort(1, [{P, {queue, [X], [], 1}} | Queues1])];
- _ ->
- lists:keysort(1, [{P, {queue, [X], [], 1}} | Queues])
- end
- end}.
-
-out({queue, [], [], 0} = Q) ->
- {empty, Q};
-out({queue, [V], [], 1}) ->
- {{value, V}, {queue, [], [], 0}};
-out({queue, [Y|In], [], Len}) ->
- [V|Out] = lists:reverse(In, []),
- {{value, V}, {queue, [Y], Out, Len - 1}};
-out({queue, In, [V], Len}) when is_list(In) ->
- {{value,V}, r2f(In, Len - 1)};
-out({queue, In,[V|Out], Len}) when is_list(In) ->
- {{value, V}, {queue, In, Out, Len - 1}};
-out({pqueue, [{P, Q} | Queues]}) ->
- {R, Q1} = out(Q),
- NewQ = case is_empty(Q1) of
- true -> case Queues of
- [] -> {queue, [], [], 0};
- [{0, OnlyQ}] -> OnlyQ;
- [_|_] -> {pqueue, Queues}
- end;
- false -> {pqueue, [{P, Q1} | Queues]}
- end,
- {R, NewQ}.
-
-out_p({queue, _, _, _} = Q) -> add_p(out(Q), 0);
-out_p({pqueue, [{P, _} | _]} = Q) -> add_p(out(Q), maybe_negate_priority(P)).
-
-add_p(R, P) -> case R of
- {empty, Q} -> {empty, Q};
- {{value, V}, Q} -> {{value, V, P}, Q}
- end.
-
-join(A, {queue, [], [], 0}) ->
- A;
-join({queue, [], [], 0}, B) ->
- B;
-join({queue, AIn, AOut, ALen}, {queue, BIn, BOut, BLen}) ->
- {queue, BIn, AOut ++ lists:reverse(AIn, BOut), ALen + BLen};
-join(A = {queue, _, _, _}, {pqueue, BPQ}) ->
- {Pre, Post} =
- lists:splitwith(fun ({P, _}) -> P < 0 orelse P == infinity end, BPQ),
- Post1 = case Post of
- [] -> [ {0, A} ];
- [ {0, ZeroQueue} | Rest ] -> [ {0, join(A, ZeroQueue)} | Rest ];
- _ -> [ {0, A} | Post ]
- end,
- {pqueue, Pre ++ Post1};
-join({pqueue, APQ}, B = {queue, _, _, _}) ->
- {Pre, Post} =
- lists:splitwith(fun ({P, _}) -> P < 0 orelse P == infinity end, APQ),
- Post1 = case Post of
- [] -> [ {0, B} ];
- [ {0, ZeroQueue} | Rest ] -> [ {0, join(ZeroQueue, B)} | Rest ];
- _ -> [ {0, B} | Post ]
- end,
- {pqueue, Pre ++ Post1};
-join({pqueue, APQ}, {pqueue, BPQ}) ->
- {pqueue, merge(APQ, BPQ, [])}.
-
-merge([], BPQ, Acc) ->
- lists:reverse(Acc, BPQ);
-merge(APQ, [], Acc) ->
- lists:reverse(Acc, APQ);
-merge([{P, A}|As], [{P, B}|Bs], Acc) ->
- merge(As, Bs, [ {P, join(A, B)} | Acc ]);
-merge([{PA, A}|As], Bs = [{PB, _}|_], Acc) when PA < PB orelse PA == infinity ->
- merge(As, Bs, [ {PA, A} | Acc ]);
-merge(As = [{_, _}|_], [{PB, B}|Bs], Acc) ->
- merge(As, Bs, [ {PB, B} | Acc ]).
-
-filter(Pred, Q) -> fold(fun(V, P, Acc) ->
- case Pred(V) of
- true -> in(V, P, Acc);
- false -> Acc
- end
- end, new(), Q).
-
-fold(Fun, Init, Q) -> case out_p(Q) of
- {empty, _Q} -> Init;
- {{value, V, P}, Q1} -> fold(Fun, Fun(V, P, Init), Q1)
- end.
-
-highest({queue, [], [], 0}) -> empty;
-highest({queue, _, _, _}) -> 0;
-highest({pqueue, [{P, _} | _]}) -> maybe_negate_priority(P).
-
-r2f([], 0) -> {queue, [], [], 0};
-r2f([_] = R, 1) -> {queue, [], R, 1};
-r2f([X,Y], 2) -> {queue, [X], [Y], 2};
-r2f([X,Y|R], L) -> {queue, [X,Y], lists:reverse(R, []), L}.
-
-maybe_negate_priority(infinity) -> infinity;
-maybe_negate_priority(P) -> -P.
diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl
deleted file mode 100644
index 65e4255a73..0000000000
--- a/src/rabbit_amqqueue.erl
+++ /dev/null
@@ -1,908 +0,0 @@
-%% The contents of this file are subject to the Mozilla Public License
-%% Version 1.1 (the "License"); you may not use this file except in
-%% compliance with the License. You may obtain a copy of the License
-%% at http://www.mozilla.org/MPL/
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and
-%% limitations under the License.
-%%
-%% The Original Code is RabbitMQ.
-%%
-%% The Initial Developer of the Original Code is GoPivotal, Inc.
-%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved.
-%%
-
--module(rabbit_amqqueue).
-
--export([recover/0, stop/0, start/1, declare/5, declare/6,
- delete_immediately/1, delete/3, purge/1, forget_all_durable/1,
- delete_crashed/1, delete_crashed_internal/1]).
--export([pseudo_queue/2, immutable/1]).
--export([lookup/1, not_found_or_absent/1, with/2, with/3, with_or_die/2,
- assert_equivalence/5,
- check_exclusive_access/2, with_exclusive_access_or_die/3,
- stat/1, deliver/2, requeue/3, ack/3, reject/4]).
--export([list/0, list/1, info_keys/0, info/1, info/2, info_all/1, info_all/2]).
--export([list_down/1]).
--export([force_event_refresh/1, notify_policy_changed/1]).
--export([consumers/1, consumers_all/1, consumer_info_keys/0]).
--export([basic_get/4, basic_consume/10, basic_cancel/4, notify_decorators/1]).
--export([notify_sent/2, notify_sent_queue_down/1, resume/2]).
--export([notify_down_all/2, activate_limit_all/2, credit/5]).
--export([on_node_up/1, on_node_down/1]).
--export([update/2, store_queue/1, update_decorators/1, policy_changed/2]).
--export([start_mirroring/1, stop_mirroring/1, sync_mirrors/1,
- cancel_sync_mirrors/1]).
-
-%% internal
--export([internal_declare/2, internal_delete/1, run_backing_queue/3,
- set_ram_duration_target/2, set_maximum_since_use/2]).
-
--include("rabbit.hrl").
--include_lib("stdlib/include/qlc.hrl").
-
--define(INTEGER_ARG_TYPES, [byte, short, signedint, long]).
-
--define(MORE_CONSUMER_CREDIT_AFTER, 50).
-
-%%----------------------------------------------------------------------------
-
--ifdef(use_specs).
-
--export_type([name/0, qmsg/0, absent_reason/0]).
-
--type(name() :: rabbit_types:r('queue')).
--type(qpids() :: [pid()]).
--type(qlen() :: rabbit_types:ok(non_neg_integer())).
--type(qfun(A) :: fun ((rabbit_types:amqqueue()) -> A | no_return())).
--type(qmsg() :: {name(), pid(), msg_id(), boolean(), rabbit_types:message()}).
--type(msg_id() :: non_neg_integer()).
--type(ok_or_errors() ::
- 'ok' | {'error', [{'error' | 'exit' | 'throw', any()}]}).
--type(absent_reason() :: 'nodedown' | 'crashed').
--type(queue_or_absent() :: rabbit_types:amqqueue() |
- {'absent', rabbit_types:amqqueue(),absent_reason()}).
--type(not_found_or_absent() ::
- 'not_found' | {'absent', rabbit_types:amqqueue(), absent_reason()}).
--spec(recover/0 :: () -> [rabbit_types:amqqueue()]).
--spec(stop/0 :: () -> 'ok').
--spec(start/1 :: ([rabbit_types:amqqueue()]) -> 'ok').
--spec(declare/5 ::
- (name(), boolean(), boolean(),
- rabbit_framing:amqp_table(), rabbit_types:maybe(pid()))
- -> {'new' | 'existing' | 'absent' | 'owner_died',
- rabbit_types:amqqueue()} | rabbit_types:channel_exit()).
--spec(declare/6 ::
- (name(), boolean(), boolean(),
- rabbit_framing:amqp_table(), rabbit_types:maybe(pid()), node())
- -> {'new' | 'existing' | 'owner_died', rabbit_types:amqqueue()} |
- {'absent', rabbit_types:amqqueue(), absent_reason()} |
- rabbit_types:channel_exit()).
--spec(internal_declare/2 ::
- (rabbit_types:amqqueue(), boolean())
- -> queue_or_absent() | rabbit_misc:thunk(queue_or_absent())).
--spec(update/2 ::
- (name(),
- fun((rabbit_types:amqqueue()) -> rabbit_types:amqqueue()))
- -> 'not_found' | rabbit_types:amqqueue()).
--spec(lookup/1 ::
- (name()) -> rabbit_types:ok(rabbit_types:amqqueue()) |
- rabbit_types:error('not_found');
- ([name()]) -> [rabbit_types:amqqueue()]).
--spec(not_found_or_absent/1 :: (name()) -> not_found_or_absent()).
--spec(with/2 :: (name(), qfun(A)) ->
- A | rabbit_types:error(not_found_or_absent())).
--spec(with/3 :: (name(), qfun(A), fun((not_found_or_absent()) -> B)) -> A | B).
--spec(with_or_die/2 ::
- (name(), qfun(A)) -> A | rabbit_types:channel_exit()).
--spec(assert_equivalence/5 ::
- (rabbit_types:amqqueue(), boolean(), boolean(),
- rabbit_framing:amqp_table(), rabbit_types:maybe(pid()))
- -> 'ok' | rabbit_types:channel_exit() |
- rabbit_types:connection_exit()).
--spec(check_exclusive_access/2 ::
- (rabbit_types:amqqueue(), pid())
- -> 'ok' | rabbit_types:channel_exit()).
--spec(with_exclusive_access_or_die/3 ::
- (name(), pid(), qfun(A)) -> A | rabbit_types:channel_exit()).
--spec(list/0 :: () -> [rabbit_types:amqqueue()]).
--spec(list/1 :: (rabbit_types:vhost()) -> [rabbit_types:amqqueue()]).
--spec(list_down/1 :: (rabbit_types:vhost()) -> [rabbit_types:amqqueue()]).
--spec(info_keys/0 :: () -> rabbit_types:info_keys()).
--spec(info/1 :: (rabbit_types:amqqueue()) -> rabbit_types:infos()).
--spec(info/2 ::
- (rabbit_types:amqqueue(), rabbit_types:info_keys())
- -> rabbit_types:infos()).
--spec(info_all/1 :: (rabbit_types:vhost()) -> [rabbit_types:infos()]).
--spec(info_all/2 :: (rabbit_types:vhost(), rabbit_types:info_keys())
- -> [rabbit_types:infos()]).
--spec(force_event_refresh/1 :: (reference()) -> 'ok').
--spec(notify_policy_changed/1 :: (rabbit_types:amqqueue()) -> 'ok').
--spec(consumers/1 :: (rabbit_types:amqqueue())
- -> [{pid(), rabbit_types:ctag(), boolean(),
- non_neg_integer(), rabbit_framing:amqp_table()}]).
--spec(consumer_info_keys/0 :: () -> rabbit_types:info_keys()).
--spec(consumers_all/1 ::
- (rabbit_types:vhost())
- -> [{name(), pid(), rabbit_types:ctag(), boolean(),
- non_neg_integer(), rabbit_framing:amqp_table()}]).
--spec(stat/1 ::
- (rabbit_types:amqqueue())
- -> {'ok', non_neg_integer(), non_neg_integer()}).
--spec(delete_immediately/1 :: (qpids()) -> 'ok').
--spec(delete/3 ::
- (rabbit_types:amqqueue(), 'false', 'false')
- -> qlen();
- (rabbit_types:amqqueue(), 'true' , 'false')
- -> qlen() | rabbit_types:error('in_use');
- (rabbit_types:amqqueue(), 'false', 'true' )
- -> qlen() | rabbit_types:error('not_empty');
- (rabbit_types:amqqueue(), 'true' , 'true' )
- -> qlen() |
- rabbit_types:error('in_use') |
- rabbit_types:error('not_empty')).
--spec(delete_crashed/1 :: (rabbit_types:amqqueue()) -> 'ok').
--spec(delete_crashed_internal/1 :: (rabbit_types:amqqueue()) -> 'ok').
--spec(purge/1 :: (rabbit_types:amqqueue()) -> qlen()).
--spec(forget_all_durable/1 :: (node()) -> 'ok').
--spec(deliver/2 :: ([rabbit_types:amqqueue()], rabbit_types:delivery()) ->
- qpids()).
--spec(requeue/3 :: (pid(), [msg_id()], pid()) -> 'ok').
--spec(ack/3 :: (pid(), [msg_id()], pid()) -> 'ok').
--spec(reject/4 :: (pid(), [msg_id()], boolean(), pid()) -> 'ok').
--spec(notify_down_all/2 :: (qpids(), pid()) -> ok_or_errors()).
--spec(activate_limit_all/2 :: (qpids(), pid()) -> ok_or_errors()).
--spec(basic_get/4 :: (rabbit_types:amqqueue(), pid(), boolean(), pid()) ->
- {'ok', non_neg_integer(), qmsg()} | 'empty').
--spec(credit/5 :: (rabbit_types:amqqueue(), pid(), rabbit_types:ctag(),
- non_neg_integer(), boolean()) -> 'ok').
--spec(basic_consume/10 ::
- (rabbit_types:amqqueue(), boolean(), pid(), pid(), boolean(),
- non_neg_integer(), rabbit_types:ctag(), boolean(),
- rabbit_framing:amqp_table(), any())
- -> rabbit_types:ok_or_error('exclusive_consume_unavailable')).
--spec(basic_cancel/4 ::
- (rabbit_types:amqqueue(), pid(), rabbit_types:ctag(), any()) -> 'ok').
--spec(notify_decorators/1 :: (rabbit_types:amqqueue()) -> 'ok').
--spec(notify_sent/2 :: (pid(), pid()) -> 'ok').
--spec(notify_sent_queue_down/1 :: (pid()) -> 'ok').
--spec(resume/2 :: (pid(), pid()) -> 'ok').
--spec(internal_delete/1 ::
- (name()) -> rabbit_types:ok_or_error('not_found') |
- rabbit_types:connection_exit() |
- fun (() -> rabbit_types:ok_or_error('not_found') |
- rabbit_types:connection_exit())).
--spec(run_backing_queue/3 ::
- (pid(), atom(),
- (fun ((atom(), A) -> {[rabbit_types:msg_id()], A}))) -> 'ok').
--spec(set_ram_duration_target/2 :: (pid(), number() | 'infinity') -> 'ok').
--spec(set_maximum_since_use/2 :: (pid(), non_neg_integer()) -> 'ok').
--spec(on_node_up/1 :: (node()) -> 'ok').
--spec(on_node_down/1 :: (node()) -> 'ok').
--spec(pseudo_queue/2 :: (name(), pid()) -> rabbit_types:amqqueue()).
--spec(immutable/1 :: (rabbit_types:amqqueue()) -> rabbit_types:amqqueue()).
--spec(store_queue/1 :: (rabbit_types:amqqueue()) -> 'ok').
--spec(update_decorators/1 :: (name()) -> 'ok').
--spec(policy_changed/2 ::
- (rabbit_types:amqqueue(), rabbit_types:amqqueue()) -> 'ok').
--spec(start_mirroring/1 :: (pid()) -> 'ok').
--spec(stop_mirroring/1 :: (pid()) -> 'ok').
--spec(sync_mirrors/1 :: (pid()) -> 'ok' | rabbit_types:error('not_mirrored')).
--spec(cancel_sync_mirrors/1 :: (pid()) -> 'ok' | {'ok', 'not_syncing'}).
-
--endif.
-
-%%----------------------------------------------------------------------------
-
--define(CONSUMER_INFO_KEYS,
- [queue_name, channel_pid, consumer_tag, ack_required, prefetch_count,
- arguments]).
-
-recover() ->
- %% Clear out remnants of old incarnation, in case we restarted
- %% faster than other nodes handled DOWN messages from us.
- on_node_down(node()),
- DurableQueues = find_durable_queues(),
- {ok, BQ} = application:get_env(rabbit, backing_queue_module),
-
- %% We rely on BQ:start/1 returning the recovery terms in the same
- %% order as the supplied queue names, so that we can zip them together
- %% for further processing in recover_durable_queues.
- {ok, OrderedRecoveryTerms} =
- BQ:start([QName || #amqqueue{name = QName} <- DurableQueues]),
- {ok,_} = supervisor:start_child(
- rabbit_sup,
- {rabbit_amqqueue_sup_sup,
- {rabbit_amqqueue_sup_sup, start_link, []},
- transient, infinity, supervisor, [rabbit_amqqueue_sup_sup]}),
- recover_durable_queues(lists:zip(DurableQueues, OrderedRecoveryTerms)).
-
-stop() ->
- ok = supervisor:terminate_child(rabbit_sup, rabbit_amqqueue_sup_sup),
- ok = supervisor:delete_child(rabbit_sup, rabbit_amqqueue_sup_sup),
- {ok, BQ} = application:get_env(rabbit, backing_queue_module),
- ok = BQ:stop().
-
-start(Qs) ->
- %% At this point all recovered queues and their bindings are
- %% visible to routing, so now it is safe for them to complete
- %% their initialisation (which may involve interacting with other
- %% queues).
- [Pid ! {self(), go} || #amqqueue{pid = Pid} <- Qs],
- ok.
-
-find_durable_queues() ->
- Node = node(),
- mnesia:async_dirty(
- fun () ->
- qlc:e(qlc:q([Q || Q = #amqqueue{name = Name,
- pid = Pid}
- <- mnesia:table(rabbit_durable_queue),
- node(Pid) == Node,
- mnesia:read(rabbit_queue, Name, read) =:= []]))
- end).
-
-recover_durable_queues(QueuesAndRecoveryTerms) ->
- {Results, Failures} =
- gen_server2:mcall(
- [{rabbit_amqqueue_sup_sup:start_queue_process(node(), Q, recovery),
- {init, {self(), Terms}}} || {Q, Terms} <- QueuesAndRecoveryTerms]),
- [rabbit_log:error("Queue ~p failed to initialise: ~p~n",
- [Pid, Error]) || {Pid, Error} <- Failures],
- [Q || {_, {new, Q}} <- Results].
-
-declare(QueueName, Durable, AutoDelete, Args, Owner) ->
- declare(QueueName, Durable, AutoDelete, Args, Owner, node()).
-
-
-%% The Node argument suggests where the queue (master if mirrored)
-%% should be. Note that in some cases (e.g. with "nodes" policy in
-%% effect) this might not be possible to satisfy.
-declare(QueueName, Durable, AutoDelete, Args, Owner, Node) ->
- ok = check_declare_arguments(QueueName, Args),
- Q = rabbit_queue_decorator:set(
- rabbit_policy:set(#amqqueue{name = QueueName,
- durable = Durable,
- auto_delete = AutoDelete,
- arguments = Args,
- exclusive_owner = Owner,
- pid = none,
- slave_pids = [],
- sync_slave_pids = [],
- recoverable_slaves = [],
- gm_pids = [],
- state = live})),
-
- Node1 = case rabbit_queue_master_location_misc:get_location(Q) of
- {ok, Node0} -> Node0;
- {error, _} -> Node
- end,
-
- Node1 = rabbit_mirror_queue_misc:initial_queue_node(Q, Node1),
- gen_server2:call(
- rabbit_amqqueue_sup_sup:start_queue_process(Node1, Q, declare),
- {init, new}, infinity).
-
-internal_declare(Q, true) ->
- rabbit_misc:execute_mnesia_tx_with_tail(
- fun () ->
- ok = store_queue(Q#amqqueue{state = live}),
- rabbit_misc:const(Q)
- end);
-internal_declare(Q = #amqqueue{name = QueueName}, false) ->
- rabbit_misc:execute_mnesia_tx_with_tail(
- fun () ->
- case mnesia:wread({rabbit_queue, QueueName}) of
- [] ->
- case not_found_or_absent(QueueName) of
- not_found -> Q1 = rabbit_policy:set(Q),
- Q2 = Q1#amqqueue{state = live},
- ok = store_queue(Q2),
- B = add_default_binding(Q1),
- fun () -> B(), Q1 end;
- {absent, _Q, _} = R -> rabbit_misc:const(R)
- end;
- [ExistingQ] ->
- rabbit_misc:const(ExistingQ)
- end
- end).
-
-update(Name, Fun) ->
- case mnesia:wread({rabbit_queue, Name}) of
- [Q = #amqqueue{durable = Durable}] ->
- Q1 = Fun(Q),
- ok = mnesia:write(rabbit_queue, Q1, write),
- case Durable of
- true -> ok = mnesia:write(rabbit_durable_queue, Q1, write);
- _ -> ok
- end,
- Q1;
- [] ->
- not_found
- end.
-
-store_queue(Q = #amqqueue{durable = true}) ->
- ok = mnesia:write(rabbit_durable_queue,
- Q#amqqueue{slave_pids = [],
- sync_slave_pids = [],
- gm_pids = [],
- decorators = undefined}, write),
- store_queue_ram(Q);
-store_queue(Q = #amqqueue{durable = false}) ->
- store_queue_ram(Q).
-
-store_queue_ram(Q) ->
- ok = mnesia:write(rabbit_queue, rabbit_queue_decorator:set(Q), write).
-
-update_decorators(Name) ->
- rabbit_misc:execute_mnesia_transaction(
- fun() ->
- case mnesia:wread({rabbit_queue, Name}) of
- [Q] -> store_queue_ram(Q),
- ok;
- [] -> ok
- end
- end).
-
-policy_changed(Q1 = #amqqueue{decorators = Decorators1},
- Q2 = #amqqueue{decorators = Decorators2}) ->
- rabbit_mirror_queue_misc:update_mirrors(Q1, Q2),
- D1 = rabbit_queue_decorator:select(Decorators1),
- D2 = rabbit_queue_decorator:select(Decorators2),
- [ok = M:policy_changed(Q1, Q2) || M <- lists:usort(D1 ++ D2)],
- %% Make sure we emit a stats event even if nothing
- %% mirroring-related has changed - the policy may have changed anyway.
- notify_policy_changed(Q1).
-
-add_default_binding(#amqqueue{name = QueueName}) ->
- ExchangeName = rabbit_misc:r(QueueName, exchange, <<>>),
- RoutingKey = QueueName#resource.name,
- rabbit_binding:add(#binding{source = ExchangeName,
- destination = QueueName,
- key = RoutingKey,
- args = []}).
-
-lookup([]) -> []; %% optimisation
-lookup([Name]) -> ets:lookup(rabbit_queue, Name); %% optimisation
-lookup(Names) when is_list(Names) ->
- %% Normally we'd call mnesia:dirty_read/1 here, but that is quite
- %% expensive for reasons explained in rabbit_misc:dirty_read/1.
- lists:append([ets:lookup(rabbit_queue, Name) || Name <- Names]);
-lookup(Name) ->
- rabbit_misc:dirty_read({rabbit_queue, Name}).
-
-not_found_or_absent(Name) ->
- %% NB: we assume that the caller has already performed a lookup on
- %% rabbit_queue and not found anything
- case mnesia:read({rabbit_durable_queue, Name}) of
- [] -> not_found;
- [Q] -> {absent, Q, nodedown} %% Q exists on stopped node
- end.
-
-not_found_or_absent_dirty(Name) ->
- %% We should read from both tables inside a tx, to get a
- %% consistent view. But the chances of an inconsistency are small,
- %% and only affect the error kind.
- case rabbit_misc:dirty_read({rabbit_durable_queue, Name}) of
- {error, not_found} -> not_found;
- {ok, Q} -> {absent, Q, nodedown}
- end.
-
-with(Name, F, E) ->
- case lookup(Name) of
- {ok, Q = #amqqueue{state = crashed}} ->
- E({absent, Q, crashed});
- {ok, Q = #amqqueue{pid = QPid}} ->
- %% We check is_process_alive(QPid) in case we receive a
- %% nodedown (for example) in F() that has nothing to do
- %% with the QPid. F() should be written s.t. that this
- %% cannot happen, so we bail if it does since that
- %% indicates a code bug and we don't want to get stuck in
- %% the retry loop.
- rabbit_misc:with_exit_handler(
- fun () -> false = rabbit_mnesia:is_process_alive(QPid),
- timer:sleep(25),
- with(Name, F, E)
- end, fun () -> F(Q) end);
- {error, not_found} ->
- E(not_found_or_absent_dirty(Name))
- end.
-
-with(Name, F) -> with(Name, F, fun (E) -> {error, E} end).
-
-with_or_die(Name, F) ->
- with(Name, F, fun (not_found) -> rabbit_misc:not_found(Name);
- ({absent, Q, Reason}) -> rabbit_misc:absent(Q, Reason)
- end).
-
-assert_equivalence(#amqqueue{name = QName,
- durable = Durable,
- auto_delete = AD} = Q,
- Durable1, AD1, Args1, Owner) ->
- rabbit_misc:assert_field_equivalence(Durable, Durable1, QName, durable),
- rabbit_misc:assert_field_equivalence(AD, AD1, QName, auto_delete),
- assert_args_equivalence(Q, Args1),
- check_exclusive_access(Q, Owner, strict).
-
-check_exclusive_access(Q, Owner) -> check_exclusive_access(Q, Owner, lax).
-
-check_exclusive_access(#amqqueue{exclusive_owner = Owner}, Owner, _MatchType) ->
- ok;
-check_exclusive_access(#amqqueue{exclusive_owner = none}, _ReaderPid, lax) ->
- ok;
-check_exclusive_access(#amqqueue{name = QueueName}, _ReaderPid, _MatchType) ->
- rabbit_misc:protocol_error(
- resource_locked,
- "cannot obtain exclusive access to locked ~s",
- [rabbit_misc:rs(QueueName)]).
-
-with_exclusive_access_or_die(Name, ReaderPid, F) ->
- with_or_die(Name,
- fun (Q) -> check_exclusive_access(Q, ReaderPid), F(Q) end).
-
-assert_args_equivalence(#amqqueue{name = QueueName, arguments = Args},
- RequiredArgs) ->
- rabbit_misc:assert_args_equivalence(Args, RequiredArgs, QueueName,
- [Key || {Key, _Fun} <- declare_args()]).
-
-check_declare_arguments(QueueName, Args) ->
- check_arguments(QueueName, Args, declare_args()).
-
-check_consume_arguments(QueueName, Args) ->
- check_arguments(QueueName, Args, consume_args()).
-
-check_arguments(QueueName, Args, Validators) ->
- [case rabbit_misc:table_lookup(Args, Key) of
- undefined -> ok;
- TypeVal -> case Fun(TypeVal, Args) of
- ok -> ok;
- {error, Error} -> rabbit_misc:protocol_error(
- precondition_failed,
- "invalid arg '~s' for ~s: ~255p",
- [Key, rabbit_misc:rs(QueueName),
- Error])
- end
- end || {Key, Fun} <- Validators],
- ok.
-
-declare_args() ->
- [{<<"x-expires">>, fun check_expires_arg/2},
- {<<"x-message-ttl">>, fun check_message_ttl_arg/2},
- {<<"x-dead-letter-exchange">>, fun check_dlxname_arg/2},
- {<<"x-dead-letter-routing-key">>, fun check_dlxrk_arg/2},
- {<<"x-max-length">>, fun check_non_neg_int_arg/2},
- {<<"x-max-length-bytes">>, fun check_non_neg_int_arg/2},
- {<<"x-max-priority">>, fun check_non_neg_int_arg/2}].
-
-consume_args() -> [{<<"x-priority">>, fun check_int_arg/2},
- {<<"x-cancel-on-ha-failover">>, fun check_bool_arg/2}].
-
-check_int_arg({Type, _}, _) ->
- case lists:member(Type, ?INTEGER_ARG_TYPES) of
- true -> ok;
- false -> {error, {unacceptable_type, Type}}
- end.
-
-check_bool_arg({bool, _}, _) -> ok;
-check_bool_arg({Type, _}, _) -> {error, {unacceptable_type, Type}}.
-
-check_non_neg_int_arg({Type, Val}, Args) ->
- case check_int_arg({Type, Val}, Args) of
- ok when Val >= 0 -> ok;
- ok -> {error, {value_negative, Val}};
- Error -> Error
- end.
-
-check_expires_arg({Type, Val}, Args) ->
- case check_int_arg({Type, Val}, Args) of
- ok when Val == 0 -> {error, {value_zero, Val}};
- ok -> rabbit_misc:check_expiry(Val);
- Error -> Error
- end.
-
-check_message_ttl_arg({Type, Val}, Args) ->
- case check_int_arg({Type, Val}, Args) of
- ok -> rabbit_misc:check_expiry(Val);
- Error -> Error
- end.
-
-%% Note that the validity of x-dead-letter-exchange is already verified
-%% by rabbit_channel's queue.declare handler.
-check_dlxname_arg({longstr, _}, _) -> ok;
-check_dlxname_arg({Type, _}, _) -> {error, {unacceptable_type, Type}}.
-
-check_dlxrk_arg({longstr, _}, Args) ->
- case rabbit_misc:table_lookup(Args, <<"x-dead-letter-exchange">>) of
- undefined -> {error, routing_key_but_no_dlx_defined};
- _ -> ok
- end;
-check_dlxrk_arg({Type, _}, _Args) ->
- {error, {unacceptable_type, Type}}.
-
-list() -> mnesia:dirty_match_object(rabbit_queue, #amqqueue{_ = '_'}).
-
-list(VHostPath) -> list(VHostPath, rabbit_queue).
-
-%% Not dirty_match_object since that would not be transactional when used in a
-%% tx context
-list(VHostPath, TableName) ->
- mnesia:async_dirty(
- fun () ->
- mnesia:match_object(
- TableName,
- #amqqueue{name = rabbit_misc:r(VHostPath, queue), _ = '_'},
- read)
- end).
-
-list_down(VHostPath) ->
- Present = list(VHostPath),
- Durable = list(VHostPath, rabbit_durable_queue),
- PresentS = sets:from_list([N || #amqqueue{name = N} <- Present]),
- sets:to_list(sets:filter(fun (#amqqueue{name = N}) ->
- not sets:is_element(N, PresentS)
- end, sets:from_list(Durable))).
-
-info_keys() -> rabbit_amqqueue_process:info_keys().
-
-map(Qs, F) -> rabbit_misc:filter_exit_map(F, Qs).
-
-info(Q = #amqqueue{ state = crashed }) -> info_down(Q, crashed);
-info(#amqqueue{ pid = QPid }) -> delegate:call(QPid, info).
-
-info(Q = #amqqueue{ state = crashed }, Items) ->
- info_down(Q, Items, crashed);
-info(#amqqueue{ pid = QPid }, Items) ->
- case delegate:call(QPid, {info, Items}) of
- {ok, Res} -> Res;
- {error, Error} -> throw(Error)
- end.
-
-info_down(Q, DownReason) ->
- info_down(Q, rabbit_amqqueue_process:info_keys(), DownReason).
-
-info_down(Q, Items, DownReason) ->
- [{Item, i_down(Item, Q, DownReason)} || Item <- Items].
-
-i_down(name, #amqqueue{name = Name}, _) -> Name;
-i_down(durable, #amqqueue{durable = Dur}, _) -> Dur;
-i_down(auto_delete, #amqqueue{auto_delete = AD}, _) -> AD;
-i_down(arguments, #amqqueue{arguments = Args}, _) -> Args;
-i_down(pid, #amqqueue{pid = QPid}, _) -> QPid;
-i_down(recoverable_slaves, #amqqueue{recoverable_slaves = RS}, _) -> RS;
-i_down(state, _Q, DownReason) -> DownReason;
-i_down(K, _Q, _DownReason) ->
- case lists:member(K, rabbit_amqqueue_process:info_keys()) of
- true -> '';
- false -> throw({bad_argument, K})
- end.
-
-info_all(VHostPath) ->
- map(list(VHostPath), fun (Q) -> info(Q) end) ++
- map(list_down(VHostPath), fun (Q) -> info_down(Q, down) end).
-
-info_all(VHostPath, Items) ->
- map(list(VHostPath), fun (Q) -> info(Q, Items) end) ++
- map(list_down(VHostPath), fun (Q) -> info_down(Q, Items, down) end).
-
-force_event_refresh(Ref) ->
- [gen_server2:cast(Q#amqqueue.pid,
- {force_event_refresh, Ref}) || Q <- list()],
- ok.
-
-notify_policy_changed(#amqqueue{pid = QPid}) ->
- gen_server2:cast(QPid, policy_changed).
-
-consumers(#amqqueue{ pid = QPid }) -> delegate:call(QPid, consumers).
-
-consumer_info_keys() -> ?CONSUMER_INFO_KEYS.
-
-consumers_all(VHostPath) ->
- ConsumerInfoKeys=consumer_info_keys(),
- lists:append(
- map(list(VHostPath),
- fun (Q) ->
- [lists:zip(
- ConsumerInfoKeys,
- [Q#amqqueue.name, ChPid, CTag, AckRequired, Prefetch, Args]) ||
- {ChPid, CTag, AckRequired, Prefetch, Args} <- consumers(Q)]
- end)).
-
-stat(#amqqueue{pid = QPid}) -> delegate:call(QPid, stat).
-
-delete_immediately(QPids) ->
- [gen_server2:cast(QPid, delete_immediately) || QPid <- QPids],
- ok.
-
-delete(#amqqueue{ pid = QPid }, IfUnused, IfEmpty) ->
- delegate:call(QPid, {delete, IfUnused, IfEmpty}).
-
-delete_crashed(#amqqueue{ pid = QPid } = Q) ->
- ok = rpc:call(node(QPid), ?MODULE, delete_crashed_internal, [Q]).
-
-delete_crashed_internal(Q = #amqqueue{ name = QName }) ->
- {ok, BQ} = application:get_env(rabbit, backing_queue_module),
- BQ:delete_crashed(Q),
- ok = internal_delete(QName).
-
-purge(#amqqueue{ pid = QPid }) -> delegate:call(QPid, purge).
-
-requeue(QPid, MsgIds, ChPid) -> delegate:call(QPid, {requeue, MsgIds, ChPid}).
-
-ack(QPid, MsgIds, ChPid) -> delegate:cast(QPid, {ack, MsgIds, ChPid}).
-
-reject(QPid, Requeue, MsgIds, ChPid) ->
- delegate:cast(QPid, {reject, Requeue, MsgIds, ChPid}).
-
-notify_down_all(QPids, ChPid) ->
- {_, Bads} = delegate:call(QPids, {notify_down, ChPid}),
- case lists:filter(
- fun ({_Pid, {exit, {R, _}, _}}) -> rabbit_misc:is_abnormal_exit(R);
- ({_Pid, _}) -> false
- end, Bads) of
- [] -> ok;
- Bads1 -> {error, Bads1}
- end.
-
-activate_limit_all(QPids, ChPid) ->
- delegate:cast(QPids, {activate_limit, ChPid}).
-
-credit(#amqqueue{pid = QPid}, ChPid, CTag, Credit, Drain) ->
- delegate:cast(QPid, {credit, ChPid, CTag, Credit, Drain}).
-
-basic_get(#amqqueue{pid = QPid}, ChPid, NoAck, LimiterPid) ->
- delegate:call(QPid, {basic_get, ChPid, NoAck, LimiterPid}).
-
-basic_consume(#amqqueue{pid = QPid, name = QName}, NoAck, ChPid, LimiterPid,
- LimiterActive, ConsumerPrefetchCount, ConsumerTag,
- ExclusiveConsume, Args, OkMsg) ->
- ok = check_consume_arguments(QName, Args),
- delegate:call(QPid, {basic_consume, NoAck, ChPid, LimiterPid, LimiterActive,
- ConsumerPrefetchCount, ConsumerTag, ExclusiveConsume,
- Args, OkMsg}).
-
-basic_cancel(#amqqueue{pid = QPid}, ChPid, ConsumerTag, OkMsg) ->
- delegate:call(QPid, {basic_cancel, ChPid, ConsumerTag, OkMsg}).
-
-notify_decorators(#amqqueue{pid = QPid}) ->
- delegate:cast(QPid, notify_decorators).
-
-notify_sent(QPid, ChPid) ->
- Key = {consumer_credit_to, QPid},
- put(Key, case get(Key) of
- 1 -> gen_server2:cast(
- QPid, {notify_sent, ChPid,
- ?MORE_CONSUMER_CREDIT_AFTER}),
- ?MORE_CONSUMER_CREDIT_AFTER;
- undefined -> erlang:monitor(process, QPid),
- ?MORE_CONSUMER_CREDIT_AFTER - 1;
- C -> C - 1
- end),
- ok.
-
-notify_sent_queue_down(QPid) ->
- erase({consumer_credit_to, QPid}),
- ok.
-
-resume(QPid, ChPid) -> delegate:cast(QPid, {resume, ChPid}).
-
-internal_delete1(QueueName, OnlyDurable) ->
- ok = mnesia:delete({rabbit_queue, QueueName}),
- %% this 'guarded' delete prevents unnecessary writes to the mnesia
- %% disk log
- case mnesia:wread({rabbit_durable_queue, QueueName}) of
- [] -> ok;
- [_] -> ok = mnesia:delete({rabbit_durable_queue, QueueName})
- end,
- %% we want to execute some things, as decided by rabbit_exchange,
- %% after the transaction.
- rabbit_binding:remove_for_destination(QueueName, OnlyDurable).
-
-internal_delete(QueueName) ->
- rabbit_misc:execute_mnesia_tx_with_tail(
- fun () ->
- case {mnesia:wread({rabbit_queue, QueueName}),
- mnesia:wread({rabbit_durable_queue, QueueName})} of
- {[], []} ->
- rabbit_misc:const({error, not_found});
- _ ->
- Deletions = internal_delete1(QueueName, false),
- T = rabbit_binding:process_deletions(Deletions),
- fun() ->
- ok = T(),
- ok = rabbit_event:notify(queue_deleted,
- [{name, QueueName}])
- end
- end
- end).
-
-forget_all_durable(Node) ->
- %% Note rabbit is not running so we avoid e.g. the worker pool. Also why
- %% we don't invoke the return from rabbit_binding:process_deletions/1.
- {atomic, ok} =
- mnesia:sync_transaction(
- fun () ->
- Qs = mnesia:match_object(rabbit_durable_queue,
- #amqqueue{_ = '_'}, write),
- [forget_node_for_queue(Node, Q) ||
- #amqqueue{pid = Pid} = Q <- Qs,
- node(Pid) =:= Node],
- ok
- end),
- ok.
-
-%% Try to promote a slave while down - it should recover as a
-%% master. We try to take the oldest slave here for best chance of
-%% recovery.
-forget_node_for_queue(DeadNode, Q = #amqqueue{recoverable_slaves = RS}) ->
- forget_node_for_queue(DeadNode, RS, Q).
-
-forget_node_for_queue(_DeadNode, [], #amqqueue{name = Name}) ->
- %% No slaves to recover from, queue is gone.
- %% Don't process_deletions since that just calls callbacks and we
- %% are not really up.
- internal_delete1(Name, true);
-
-%% Should not happen, but let's be conservative.
-forget_node_for_queue(DeadNode, [DeadNode | T], Q) ->
- forget_node_for_queue(DeadNode, T, Q);
-
-forget_node_for_queue(DeadNode, [H|T], Q) ->
- case node_permits_offline_promotion(H) of
- false -> forget_node_for_queue(DeadNode, T, Q);
- true -> Q1 = Q#amqqueue{pid = rabbit_misc:node_to_fake_pid(H)},
- ok = mnesia:write(rabbit_durable_queue, Q1, write)
- end.
-
-node_permits_offline_promotion(Node) ->
- case node() of
- Node -> not rabbit:is_running(); %% [1]
- _ -> Running = rabbit_mnesia:cluster_nodes(running),
- not lists:member(Node, Running) %% [2]
- end.
-%% [1] In this case if we are a real running node (i.e. rabbitmqctl
-%% has RPCed into us) then we cannot allow promotion. If on the other
-%% hand we *are* rabbitmqctl impersonating the node for offline
-%% node-forgetting then we can.
-%%
-%% [2] This is simpler; as long as it's down that's OK
-
-run_backing_queue(QPid, Mod, Fun) ->
- gen_server2:cast(QPid, {run_backing_queue, Mod, Fun}).
-
-set_ram_duration_target(QPid, Duration) ->
- gen_server2:cast(QPid, {set_ram_duration_target, Duration}).
-
-set_maximum_since_use(QPid, Age) ->
- gen_server2:cast(QPid, {set_maximum_since_use, Age}).
-
-start_mirroring(QPid) -> ok = delegate:cast(QPid, start_mirroring).
-stop_mirroring(QPid) -> ok = delegate:cast(QPid, stop_mirroring).
-
-sync_mirrors(QPid) -> delegate:call(QPid, sync_mirrors).
-cancel_sync_mirrors(QPid) -> delegate:call(QPid, cancel_sync_mirrors).
-
-on_node_up(Node) ->
- ok = rabbit_misc:execute_mnesia_transaction(
- fun () ->
- Qs = mnesia:match_object(rabbit_queue,
- #amqqueue{_ = '_'}, write),
- [maybe_clear_recoverable_node(Node, Q) || Q <- Qs],
- ok
- end).
-
-maybe_clear_recoverable_node(Node,
- #amqqueue{sync_slave_pids = SPids,
- recoverable_slaves = RSs} = Q) ->
- case lists:member(Node, RSs) of
- true ->
- %% There is a race with
- %% rabbit_mirror_queue_slave:record_synchronised/1 called
- %% by the incoming slave node and this function, called
- %% by the master node. If this function is executed after
- %% record_synchronised/1, the node is erroneously removed
- %% from the recoverable slaves list.
- %%
- %% We check if the slave node's queue PID is alive. If it is
- %% the case, then this function is executed after. In this
- %% situation, we don't touch the queue record, it is already
- %% correct.
- DoClearNode =
- case [SP || SP <- SPids, node(SP) =:= Node] of
- [SPid] -> not rabbit_misc:is_process_alive(SPid);
- _ -> true
- end,
- if
- DoClearNode -> RSs1 = RSs -- [Node],
- store_queue(
- Q#amqqueue{recoverable_slaves = RSs1});
- true -> ok
- end;
- false ->
- ok
- end.
-
-on_node_down(Node) ->
- rabbit_misc:execute_mnesia_tx_with_tail(
- fun () -> QsDels =
- qlc:e(qlc:q([{QName, delete_queue(QName)} ||
- #amqqueue{name = QName, pid = Pid,
- slave_pids = []}
- <- mnesia:table(rabbit_queue),
- node(Pid) == Node andalso
- not rabbit_mnesia:is_process_alive(Pid)])),
- {Qs, Dels} = lists:unzip(QsDels),
- T = rabbit_binding:process_deletions(
- lists:foldl(fun rabbit_binding:combine_deletions/2,
- rabbit_binding:new_deletions(), Dels)),
- fun () ->
- T(),
- lists:foreach(
- fun(QName) ->
- ok = rabbit_event:notify(queue_deleted,
- [{name, QName}])
- end, Qs)
- end
- end).
-
-delete_queue(QueueName) ->
- ok = mnesia:delete({rabbit_queue, QueueName}),
- rabbit_binding:remove_transient_for_destination(QueueName).
-
-pseudo_queue(QueueName, Pid) ->
- #amqqueue{name = QueueName,
- durable = false,
- auto_delete = false,
- arguments = [],
- pid = Pid,
- slave_pids = []}.
-
-immutable(Q) -> Q#amqqueue{pid = none,
- slave_pids = none,
- sync_slave_pids = none,
- recoverable_slaves = none,
- gm_pids = none,
- policy = none,
- decorators = none,
- state = none}.
-
-deliver([], _Delivery) ->
- %% /dev/null optimisation
- [];
-
-deliver(Qs, Delivery = #delivery{flow = Flow}) ->
- {MPids, SPids} = qpids(Qs),
- QPids = MPids ++ SPids,
- %% We use up two credits to send to a slave since the message
- %% arrives at the slave from two directions. We will ack one when
- %% the slave receives the message direct from the channel, and the
- %% other when it receives it via GM.
- case Flow of
- %% Here we are tracking messages sent by the rabbit_channel
- %% process. We are accessing the rabbit_channel process
- %% dictionary.
- flow -> [credit_flow:send(QPid) || QPid <- QPids],
- [credit_flow:send(QPid) || QPid <- SPids];
- noflow -> ok
- end,
-
- %% We let slaves know that they were being addressed as slaves at
- %% the time - if they receive such a message from the channel
- %% after they have become master they should mark the message as
- %% 'delivered' since they do not know what the master may have
- %% done with it.
- MMsg = {deliver, Delivery, false},
- SMsg = {deliver, Delivery, true},
- delegate:cast(MPids, MMsg),
- delegate:cast(SPids, SMsg),
- QPids.
-
-qpids([]) -> {[], []}; %% optimisation
-qpids([#amqqueue{pid = QPid, slave_pids = SPids}]) -> {[QPid], SPids}; %% opt
-qpids(Qs) ->
- {MPids, SPids} = lists:foldl(fun (#amqqueue{pid = QPid, slave_pids = SPids},
- {MPidAcc, SPidAcc}) ->
- {[QPid | MPidAcc], [SPids | SPidAcc]}
- end, {[], []}, Qs),
- {MPids, lists:append(SPids)}.
diff --git a/src/rabbit_auth_mechanism.erl b/src/rabbit_auth_mechanism.erl
deleted file mode 100644
index 78e3e7dd4b..0000000000
--- a/src/rabbit_auth_mechanism.erl
+++ /dev/null
@@ -1,56 +0,0 @@
-%% The contents of this file are subject to the Mozilla Public License
-%% Version 1.1 (the "License"); you may not use this file except in
-%% compliance with the License. You may obtain a copy of the License
-%% at http://www.mozilla.org/MPL/
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and
-%% limitations under the License.
-%%
-%% The Original Code is RabbitMQ.
-%%
-%% The Initial Developer of the Original Code is GoPivotal, Inc.
-%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved.
-%%
-
--module(rabbit_auth_mechanism).
-
--ifdef(use_specs).
-
-%% A description.
--callback description() -> [proplists:property()].
-
-%% If this mechanism is enabled, should it be offered for a given socket?
-%% (primarily so EXTERNAL can be SSL-only)
--callback should_offer(rabbit_net:socket()) -> boolean().
-
-%% Called before authentication starts. Should create a state
-%% object to be passed through all the stages of authentication.
--callback init(rabbit_net:socket()) -> any().
-
-%% Handle a stage of authentication. Possible responses:
-%% {ok, User}
-%% Authentication succeeded, and here's the user record.
-%% {challenge, Challenge, NextState}
-%% Another round is needed. Here's the state I want next time.
-%% {protocol_error, Msg, Args}
-%% Client got the protocol wrong. Log and die.
-%% {refused, Username, Msg, Args}
-%% Client failed authentication. Log and die.
--callback handle_response(binary(), any()) ->
- {'ok', rabbit_types:user()} |
- {'challenge', binary(), any()} |
- {'protocol_error', string(), [any()]} |
- {'refused', rabbit_types:username() | none, string(), [any()]}.
-
--else.
-
--export([behaviour_info/1]).
-
-behaviour_info(callbacks) ->
- [{description, 0}, {should_offer, 1}, {init, 1}, {handle_response, 2}];
-behaviour_info(_Other) ->
- undefined.
-
--endif.
diff --git a/src/rabbit_authn_backend.erl b/src/rabbit_authn_backend.erl
deleted file mode 100644
index b9cb0d3669..0000000000
--- a/src/rabbit_authn_backend.erl
+++ /dev/null
@@ -1,49 +0,0 @@
-%% The contents of this file are subject to the Mozilla Public License
-%% Version 1.1 (the "License"); you may not use this file except in
-%% compliance with the License. You may obtain a copy of the License
-%% at http://www.mozilla.org/MPL/
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and
-%% limitations under the License.
-%%
-%% The Original Code is RabbitMQ.
-%%
-%% The Initial Developer of the Original Code is GoPivotal, Inc.
-%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved.
-%%
-
--module(rabbit_authn_backend).
-
--include("rabbit.hrl").
-
--ifdef(use_specs).
-
-%% Check a user can log in, given a username and a proplist of
-%% authentication information (e.g. [{password, Password}]). If your
-%% backend is not to be used for authentication, this should always
-%% refuse access.
-%%
-%% Possible responses:
-%% {ok, User}
-%% Authentication succeeded, and here's the user record.
-%% {error, Error}
-%% Something went wrong. Log and die.
-%% {refused, Msg, Args}
-%% Client failed authentication. Log and die.
--callback user_login_authentication(rabbit_types:username(), [term()]) ->
- {'ok', rabbit_types:auth_user()} |
- {'refused', string(), [any()]} |
- {'error', any()}.
-
--else.
-
--export([behaviour_info/1]).
-
-behaviour_info(callbacks) ->
- [{user_login_authentication, 2}];
-behaviour_info(_Other) ->
- undefined.
-
--endif.
diff --git a/src/rabbit_authz_backend.erl b/src/rabbit_authz_backend.erl
deleted file mode 100644
index 495a79695d..0000000000
--- a/src/rabbit_authz_backend.erl
+++ /dev/null
@@ -1,76 +0,0 @@
-%% The contents of this file are subject to the Mozilla Public License
-%% Version 1.1 (the "License"); you may not use this file except in
-%% compliance with the License. You may obtain a copy of the License
-%% at http://www.mozilla.org/MPL/
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and
-%% limitations under the License.
-%%
-%% The Original Code is RabbitMQ.
-%%
-%% The Initial Developer of the Original Code is GoPivotal, Inc.
-%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved.
-%%
-
--module(rabbit_authz_backend).
-
--include("rabbit.hrl").
-
--ifdef(use_specs).
-
-%% Check a user can log in, when this backend is being used for
-%% authorisation only. Authentication has already taken place
-%% successfully, but we need to check that the user exists in this
-%% backend, and initialise any impl field we will want to have passed
-%% back in future calls to check_vhost_access/3 and
-%% check_resource_access/3.
-%%
-%% Possible responses:
-%% {ok, Impl}
-%% {ok, Impl, Tags}
-%% User authorisation succeeded, and here's the impl and potential extra tags fields.
-%% {error, Error}
-%% Something went wrong. Log and die.
-%% {refused, Msg, Args}
-%% User authorisation failed. Log and die.
--callback user_login_authorization(rabbit_types:username()) ->
- {'ok', any()} |
- {'ok', any(), any()} |
- {'refused', string(), [any()]} |
- {'error', any()}.
-
-%% Given #auth_user and vhost, can a user log in to a vhost?
-%% Possible responses:
-%% true
-%% false
-%% {error, Error}
-%% Something went wrong. Log and die.
--callback check_vhost_access(rabbit_types:auth_user(),
- rabbit_types:vhost(), rabbit_net:socket()) ->
- boolean() | {'error', any()}.
-
-%% Given #auth_user, resource and permission, can a user access a resource?
-%%
-%% Possible responses:
-%% true
-%% false
-%% {error, Error}
-%% Something went wrong. Log and die.
--callback check_resource_access(rabbit_types:auth_user(),
- rabbit_types:r(atom()),
- rabbit_access_control:permission_atom()) ->
- boolean() | {'error', any()}.
-
--else.
-
--export([behaviour_info/1]).
-
-behaviour_info(callbacks) ->
- [{user_login_authorization, 1},
- {check_vhost_access, 3}, {check_resource_access, 3}];
-behaviour_info(_Other) ->
- undefined.
-
--endif.
diff --git a/src/rabbit_backing_queue.erl b/src/rabbit_backing_queue.erl
deleted file mode 100644
index 2b808e206c..0000000000
--- a/src/rabbit_backing_queue.erl
+++ /dev/null
@@ -1,282 +0,0 @@
-%% The contents of this file are subject to the Mozilla Public License
-%% Version 1.1 (the "License"); you may not use this file except in
-%% compliance with the License. You may obtain a copy of the License
-%% at http://www.mozilla.org/MPL/
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and
-%% limitations under the License.
-%%
-%% The Original Code is RabbitMQ.
-%%
-%% The Initial Developer of the Original Code is GoPivotal, Inc.
-%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved.
-%%
-
--module(rabbit_backing_queue).
-
--export([info_keys/0]).
-
--define(INFO_KEYS, [messages_ram, messages_ready_ram,
- messages_unacknowledged_ram, messages_persistent,
- message_bytes, message_bytes_ready,
- message_bytes_unacknowledged, message_bytes_ram,
- message_bytes_persistent, head_message_timestamp,
- disk_reads, disk_writes, backing_queue_status]).
-
--ifdef(use_specs).
-
-%% We can't specify a per-queue ack/state with callback signatures
--type(ack() :: any()).
--type(state() :: any()).
-
--type(flow() :: 'flow' | 'noflow').
--type(msg_ids() :: [rabbit_types:msg_id()]).
--type(publish() :: {rabbit_types:basic_message(),
- rabbit_types:message_properties(), boolean()}).
--type(delivered_publish() :: {rabbit_types:basic_message(),
- rabbit_types:message_properties()}).
--type(fetch_result(Ack) ::
- ('empty' | {rabbit_types:basic_message(), boolean(), Ack})).
--type(drop_result(Ack) ::
- ('empty' | {rabbit_types:msg_id(), Ack})).
--type(recovery_terms() :: [term()] | 'non_clean_shutdown').
--type(recovery_info() :: 'new' | recovery_terms()).
--type(purged_msg_count() :: non_neg_integer()).
--type(async_callback() ::
- fun ((atom(), fun ((atom(), state()) -> state())) -> 'ok')).
--type(duration() :: ('undefined' | 'infinity' | number())).
-
--type(msg_fun(A) :: fun ((rabbit_types:basic_message(), ack(), A) -> A)).
--type(msg_pred() :: fun ((rabbit_types:message_properties()) -> boolean())).
-
--spec(info_keys/0 :: () -> rabbit_types:info_keys()).
-
-%% Called on startup with a list of durable queue names. The queues
-%% aren't being started at this point, but this call allows the
-%% backing queue to perform any checking necessary for the consistency
-%% of those queues, or initialise any other shared resources.
-%%
-%% The list of queue recovery terms returned as {ok, Terms} must be given
-%% in the same order as the list of queue names supplied.
--callback start([rabbit_amqqueue:name()]) -> rabbit_types:ok(recovery_terms()).
-
-%% Called to tear down any state/resources. NB: Implementations should
-%% not depend on this function being called on shutdown and instead
-%% should hook into the rabbit supervision hierarchy.
--callback stop() -> 'ok'.
-
-%% Initialise the backing queue and its state.
-%%
-%% Takes
-%% 1. the amqqueue record
-%% 2. a term indicating whether the queue is an existing queue that
-%% should be recovered or not. When 'new' is given, no recovery is
-%% taking place, otherwise a list of recovery terms is given, or
-%% the atom 'non_clean_shutdown' if no recovery terms are available.
-%% 3. an asynchronous callback which accepts a function of type
-%% backing-queue-state to backing-queue-state. This callback
-%% function can be safely invoked from any process, which makes it
-%% useful for passing messages back into the backing queue,
-%% especially as the backing queue does not have control of its own
-%% mailbox.
--callback init(rabbit_types:amqqueue(), recovery_info(),
- async_callback()) -> state().
-
-%% Called on queue shutdown when queue isn't being deleted.
--callback terminate(any(), state()) -> state().
-
-%% Called when the queue is terminating and needs to delete all its
-%% content.
--callback delete_and_terminate(any(), state()) -> state().
-
-%% Called to clean up after a crashed queue. In this case we don't
-%% have a process and thus a state(), we are just removing on-disk data.
--callback delete_crashed(rabbit_types:amqqueue()) -> 'ok'.
-
-%% Remove all 'fetchable' messages from the queue, i.e. all messages
-%% except those that have been fetched already and are pending acks.
--callback purge(state()) -> {purged_msg_count(), state()}.
-
-%% Remove all messages in the queue which have been fetched and are
-%% pending acks.
--callback purge_acks(state()) -> state().
-
-%% Publish a message.
--callback publish(rabbit_types:basic_message(),
- rabbit_types:message_properties(), boolean(), pid(), flow(),
- state()) -> state().
-
-%% Like publish/6 but for batches of publishes.
--callback batch_publish([publish()], pid(), flow(), state()) -> state().
-
-%% Called for messages which have already been passed straight
-%% out to a client. The queue will be empty for these calls
-%% (i.e. saves the round trip through the backing queue).
--callback publish_delivered(rabbit_types:basic_message(),
- rabbit_types:message_properties(), pid(), flow(),
- state())
- -> {ack(), state()}.
-
-%% Like publish_delivered/5 but for batches of publishes.
--callback batch_publish_delivered([delivered_publish()], pid(), flow(),
- state())
- -> {[ack()], state()}.
-
-%% Called to inform the BQ about messages which have reached the
-%% queue, but are not going to be further passed to BQ.
--callback discard(rabbit_types:msg_id(), pid(), flow(), state()) -> state().
-
-%% Return ids of messages which have been confirmed since the last
-%% invocation of this function (or initialisation).
-%%
-%% Message ids should only appear in the result of drain_confirmed
-%% under the following circumstances:
-%%
-%% 1. The message appears in a call to publish_delivered/4 and the
-%% first argument (ack_required) is false; or
-%% 2. The message is fetched from the queue with fetch/2 and the first
-%% argument (ack_required) is false; or
-%% 3. The message is acked (ack/2 is called for the message); or
-%% 4. The message is fully fsync'd to disk in such a way that the
-%% recovery of the message is guaranteed in the event of a crash of
-%% this rabbit node (excluding hardware failure).
-%%
-%% In addition to the above conditions, a message id may only appear
-%% in the result of drain_confirmed if
-%% #message_properties.needs_confirming = true when the msg was
-%% published (through whichever means) to the backing queue.
-%%
-%% It is legal for the same message id to appear in the results of
-%% multiple calls to drain_confirmed, which means that the backing
-%% queue is not required to keep track of which messages it has
-%% already confirmed. The confirm will be issued to the publisher the
-%% first time the message id appears in the result of
-%% drain_confirmed. All subsequent appearances of that message id will
-%% be ignored.
--callback drain_confirmed(state()) -> {msg_ids(), state()}.
-
-%% Drop messages from the head of the queue while the supplied
-%% predicate on message properties returns true. Returns the first
-%% message properties for which the predictate returned false, or
-%% 'undefined' if the whole backing queue was traversed w/o the
-%% predicate ever returning false.
--callback dropwhile(msg_pred(), state())
- -> {rabbit_types:message_properties() | undefined, state()}.
-
-%% Like dropwhile, except messages are fetched in "require
-%% acknowledgement" mode and are passed, together with their ack tag,
-%% to the supplied function. The function is also fed an
-%% accumulator. The result of fetchwhile is as for dropwhile plus the
-%% accumulator.
--callback fetchwhile(msg_pred(), msg_fun(A), A, state())
- -> {rabbit_types:message_properties() | undefined,
- A, state()}.
-
-%% Produce the next message.
--callback fetch(true, state()) -> {fetch_result(ack()), state()};
- (false, state()) -> {fetch_result(undefined), state()}.
-
-%% Remove the next message.
--callback drop(true, state()) -> {drop_result(ack()), state()};
- (false, state()) -> {drop_result(undefined), state()}.
-
-%% Acktags supplied are for messages which can now be forgotten
-%% about. Must return 1 msg_id per Ack, in the same order as Acks.
--callback ack([ack()], state()) -> {msg_ids(), state()}.
-
-%% Reinsert messages into the queue which have already been delivered
-%% and were pending acknowledgement.
--callback requeue([ack()], state()) -> {msg_ids(), state()}.
-
-%% Fold over messages by ack tag. The supplied function is called with
-%% each message, its ack tag, and an accumulator.
--callback ackfold(msg_fun(A), A, state(), [ack()]) -> {A, state()}.
-
-%% Fold over all the messages in a queue and return the accumulated
-%% results, leaving the queue undisturbed.
--callback fold(fun((rabbit_types:basic_message(),
- rabbit_types:message_properties(),
- boolean(), A) -> {('stop' | 'cont'), A}),
- A, state()) -> {A, state()}.
-
-%% How long is my queue?
--callback len(state()) -> non_neg_integer().
-
-%% Is my queue empty?
--callback is_empty(state()) -> boolean().
-
-%% What's the queue depth, where depth = length + number of pending acks
--callback depth(state()) -> non_neg_integer().
-
-%% For the next three functions, the assumption is that you're
-%% monitoring something like the ingress and egress rates of the
-%% queue. The RAM duration is thus the length of time represented by
-%% the messages held in RAM given the current rates. If you want to
-%% ignore all of this stuff, then do so, and return 0 in
-%% ram_duration/1.
-
-%% The target is to have no more messages in RAM than indicated by the
-%% duration and the current queue rates.
--callback set_ram_duration_target(duration(), state()) -> state().
-
-%% Optionally recalculate the duration internally (likely to be just
-%% update your internal rates), and report how many seconds the
-%% messages in RAM represent given the current rates of the queue.
--callback ram_duration(state()) -> {duration(), state()}.
-
-%% Should 'timeout' be called as soon as the queue process can manage
-%% (either on an empty mailbox, or when a timer fires)?
--callback needs_timeout(state()) -> 'false' | 'timed' | 'idle'.
-
-%% Called (eventually) after needs_timeout returns 'idle' or 'timed'.
-%% Note this may be called more than once for each 'idle' or 'timed'
-%% returned from needs_timeout
--callback timeout(state()) -> state().
-
-%% Called immediately before the queue hibernates.
--callback handle_pre_hibernate(state()) -> state().
-
-%% Called when more credit has become available for credit_flow.
--callback resume(state()) -> state().
-
-%% Used to help prioritisation in rabbit_amqqueue_process. The rate of
-%% inbound messages and outbound messages at the moment.
--callback msg_rates(state()) -> {float(), float()}.
-
--callback info(atom(), state()) -> any().
-
-%% Passed a function to be invoked with the relevant backing queue's
-%% state. Useful for when the backing queue or other components need
-%% to pass functions into the backing queue.
--callback invoke(atom(), fun ((atom(), A) -> A), state()) -> state().
-
-%% Called prior to a publish or publish_delivered call. Allows the BQ
-%% to signal that it's already seen this message, (e.g. it was published
-%% or discarded previously) and thus the message should be dropped.
--callback is_duplicate(rabbit_types:basic_message(), state())
- -> {boolean(), state()}.
-
--else.
-
--export([behaviour_info/1]).
-
-behaviour_info(callbacks) ->
- [{start, 1}, {stop, 0}, {init, 3}, {terminate, 2},
- {delete_and_terminate, 2}, {delete_crashed, 1}, {purge, 1},
- {purge_acks, 1}, {publish, 6}, {publish_delivered, 5},
- {batch_publish, 4}, {batch_publish_delivered, 4},
- {discard, 4}, {drain_confirmed, 1},
- {dropwhile, 2}, {fetchwhile, 4}, {fetch, 2},
- {drop, 2}, {ack, 2}, {requeue, 2}, {ackfold, 4}, {fold, 3}, {len, 1},
- {is_empty, 1}, {depth, 1}, {set_ram_duration_target, 2},
- {ram_duration, 1}, {needs_timeout, 1}, {timeout, 1},
- {handle_pre_hibernate, 1}, {resume, 1}, {msg_rates, 1},
- {info, 2}, {invoke, 3}, {is_duplicate, 2}] ;
-behaviour_info(_Other) ->
- undefined.
-
--endif.
-
-info_keys() -> ?INFO_KEYS.
diff --git a/src/rabbit_basic.erl b/src/rabbit_basic.erl
deleted file mode 100644
index d70f4f379d..0000000000
--- a/src/rabbit_basic.erl
+++ /dev/null
@@ -1,326 +0,0 @@
-%% The contents of this file are subject to the Mozilla Public License
-%% Version 1.1 (the "License"); you may not use this file except in
-%% compliance with the License. You may obtain a copy of the License
-%% at http://www.mozilla.org/MPL/
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and
-%% limitations under the License.
-%%
-%% The Original Code is RabbitMQ.
-%%
-%% The Initial Developer of the Original Code is GoPivotal, Inc.
-%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved.
-%%
-
--module(rabbit_basic).
--include("rabbit.hrl").
--include("rabbit_framing.hrl").
-
--export([publish/4, publish/5, publish/1,
- message/3, message/4, properties/1, prepend_table_header/3,
- extract_headers/1, extract_timestamp/1, map_headers/2, delivery/4,
- header_routes/1, parse_expiration/1, header/2, header/3]).
--export([build_content/2, from_content/1, msg_size/1, maybe_gc_large_msg/1]).
-
-%%----------------------------------------------------------------------------
-
--ifdef(use_specs).
-
--type(properties_input() ::
- (rabbit_framing:amqp_property_record() | [{atom(), any()}])).
--type(publish_result() ::
- ({ok, [pid()]} | rabbit_types:error('not_found'))).
--type(header() :: any()).
--type(headers() :: rabbit_framing:amqp_table() | 'undefined').
-
--type(exchange_input() :: (rabbit_types:exchange() | rabbit_exchange:name())).
--type(body_input() :: (binary() | [binary()])).
-
--spec(publish/4 ::
- (exchange_input(), rabbit_router:routing_key(), properties_input(),
- body_input()) -> publish_result()).
--spec(publish/5 ::
- (exchange_input(), rabbit_router:routing_key(), boolean(),
- properties_input(), body_input()) -> publish_result()).
--spec(publish/1 ::
- (rabbit_types:delivery()) -> publish_result()).
--spec(delivery/4 ::
- (boolean(), boolean(), rabbit_types:message(), undefined | integer()) ->
- rabbit_types:delivery()).
--spec(message/4 ::
- (rabbit_exchange:name(), rabbit_router:routing_key(),
- properties_input(), binary()) -> rabbit_types:message()).
--spec(message/3 ::
- (rabbit_exchange:name(), rabbit_router:routing_key(),
- rabbit_types:decoded_content()) ->
- rabbit_types:ok_or_error2(rabbit_types:message(), any())).
--spec(properties/1 ::
- (properties_input()) -> rabbit_framing:amqp_property_record()).
-
--spec(prepend_table_header/3 ::
- (binary(), rabbit_framing:amqp_table(), headers()) -> headers()).
-
--spec(header/2 ::
- (header(), headers()) -> 'undefined' | any()).
--spec(header/3 ::
- (header(), headers(), any()) -> 'undefined' | any()).
-
--spec(extract_headers/1 :: (rabbit_types:content()) -> headers()).
-
--spec(map_headers/2 :: (fun((headers()) -> headers()), rabbit_types:content())
- -> rabbit_types:content()).
-
--spec(header_routes/1 ::
- (undefined | rabbit_framing:amqp_table()) -> [string()]).
--spec(build_content/2 :: (rabbit_framing:amqp_property_record(),
- binary() | [binary()]) -> rabbit_types:content()).
--spec(from_content/1 :: (rabbit_types:content()) ->
- {rabbit_framing:amqp_property_record(), binary()}).
--spec(parse_expiration/1 ::
- (rabbit_framing:amqp_property_record())
- -> rabbit_types:ok_or_error2('undefined' | non_neg_integer(), any())).
-
--spec(msg_size/1 :: (rabbit_types:content() | rabbit_types:message()) ->
- non_neg_integer()).
-
--spec(maybe_gc_large_msg/1 ::
- (rabbit_types:content() | rabbit_types:message()) -> non_neg_integer()).
-
--endif.
-
-%%----------------------------------------------------------------------------
-
-%% Convenience function, for avoiding round-trips in calls across the
-%% erlang distributed network.
-publish(Exchange, RoutingKeyBin, Properties, Body) ->
- publish(Exchange, RoutingKeyBin, false, Properties, Body).
-
-%% Convenience function, for avoiding round-trips in calls across the
-%% erlang distributed network.
-publish(X = #exchange{name = XName}, RKey, Mandatory, Props, Body) ->
- Message = message(XName, RKey, properties(Props), Body),
- publish(X, delivery(Mandatory, false, Message, undefined));
-publish(XName, RKey, Mandatory, Props, Body) ->
- Message = message(XName, RKey, properties(Props), Body),
- publish(delivery(Mandatory, false, Message, undefined)).
-
-publish(Delivery = #delivery{
- message = #basic_message{exchange_name = XName}}) ->
- case rabbit_exchange:lookup(XName) of
- {ok, X} -> publish(X, Delivery);
- Err -> Err
- end.
-
-publish(X, Delivery) ->
- Qs = rabbit_amqqueue:lookup(rabbit_exchange:route(X, Delivery)),
- DeliveredQPids = rabbit_amqqueue:deliver(Qs, Delivery),
- {ok, DeliveredQPids}.
-
-delivery(Mandatory, Confirm, Message, MsgSeqNo) ->
- #delivery{mandatory = Mandatory, confirm = Confirm, sender = self(),
- message = Message, msg_seq_no = MsgSeqNo, flow = noflow}.
-
-build_content(Properties, BodyBin) when is_binary(BodyBin) ->
- build_content(Properties, [BodyBin]);
-
-build_content(Properties, PFR) ->
- %% basic.publish hasn't changed so we can just hard-code amqp_0_9_1
- {ClassId, _MethodId} =
- rabbit_framing_amqp_0_9_1:method_id('basic.publish'),
- #content{class_id = ClassId,
- properties = Properties,
- properties_bin = none,
- protocol = none,
- payload_fragments_rev = PFR}.
-
-from_content(Content) ->
- #content{class_id = ClassId,
- properties = Props,
- payload_fragments_rev = FragmentsRev} =
- rabbit_binary_parser:ensure_content_decoded(Content),
- %% basic.publish hasn't changed so we can just hard-code amqp_0_9_1
- {ClassId, _MethodId} =
- rabbit_framing_amqp_0_9_1:method_id('basic.publish'),
- {Props, list_to_binary(lists:reverse(FragmentsRev))}.
-
-%% This breaks the spec rule forbidding message modification
-strip_header(#content{properties = #'P_basic'{headers = undefined}}
- = DecodedContent, _Key) ->
- DecodedContent;
-strip_header(#content{properties = Props = #'P_basic'{headers = Headers}}
- = DecodedContent, Key) ->
- case lists:keysearch(Key, 1, Headers) of
- false -> DecodedContent;
- {value, Found} -> Headers0 = lists:delete(Found, Headers),
- rabbit_binary_generator:clear_encoded_content(
- DecodedContent#content{
- properties = Props#'P_basic'{
- headers = Headers0}})
- end.
-
-message(XName, RoutingKey, #content{properties = Props} = DecodedContent) ->
- try
- {ok, #basic_message{
- exchange_name = XName,
- content = strip_header(DecodedContent, ?DELETED_HEADER),
- id = rabbit_guid:gen(),
- is_persistent = is_message_persistent(DecodedContent),
- routing_keys = [RoutingKey |
- header_routes(Props#'P_basic'.headers)]}}
- catch
- {error, _Reason} = Error -> Error
- end.
-
-message(XName, RoutingKey, RawProperties, Body) ->
- Properties = properties(RawProperties),
- Content = build_content(Properties, Body),
- {ok, Msg} = message(XName, RoutingKey, Content),
- Msg.
-
-properties(P = #'P_basic'{}) ->
- P;
-properties(P) when is_list(P) ->
- %% Yes, this is O(length(P) * record_info(size, 'P_basic') / 2),
- %% i.e. slow. Use the definition of 'P_basic' directly if
- %% possible!
- lists:foldl(fun ({Key, Value}, Acc) ->
- case indexof(record_info(fields, 'P_basic'), Key) of
- 0 -> throw({unknown_basic_property, Key});
- N -> setelement(N + 1, Acc, Value)
- end
- end, #'P_basic'{}, P).
-
-prepend_table_header(Name, Info, undefined) ->
- prepend_table_header(Name, Info, []);
-prepend_table_header(Name, Info, Headers) ->
- case rabbit_misc:table_lookup(Headers, Name) of
- {array, Existing} ->
- prepend_table(Name, Info, Existing, Headers);
- undefined ->
- prepend_table(Name, Info, [], Headers);
- Other ->
- Headers2 = prepend_table(Name, Info, [], Headers),
- set_invalid_header(Name, Other, Headers2)
- end.
-
-prepend_table(Name, Info, Prior, Headers) ->
- rabbit_misc:set_table_value(Headers, Name, array, [{table, Info} | Prior]).
-
-set_invalid_header(Name, {_, _}=Value, Headers) when is_list(Headers) ->
- case rabbit_misc:table_lookup(Headers, ?INVALID_HEADERS_KEY) of
- undefined ->
- set_invalid([{Name, array, [Value]}], Headers);
- {table, ExistingHdr} ->
- update_invalid(Name, Value, ExistingHdr, Headers);
- Other ->
- %% somehow the x-invalid-headers header is corrupt
- Invalid = [{?INVALID_HEADERS_KEY, array, [Other]}],
- set_invalid_header(Name, Value, set_invalid(Invalid, Headers))
- end.
-
-set_invalid(NewHdr, Headers) ->
- rabbit_misc:set_table_value(Headers, ?INVALID_HEADERS_KEY, table, NewHdr).
-
-update_invalid(Name, Value, ExistingHdr, Header) ->
- Values = case rabbit_misc:table_lookup(ExistingHdr, Name) of
- undefined -> [Value];
- {array, Prior} -> [Value | Prior]
- end,
- NewHdr = rabbit_misc:set_table_value(ExistingHdr, Name, array, Values),
- set_invalid(NewHdr, Header).
-
-header(_Header, undefined) ->
- undefined;
-header(_Header, []) ->
- undefined;
-header(Header, Headers) ->
- header(Header, Headers, undefined).
-
-header(Header, Headers, Default) ->
- case lists:keysearch(Header, 1, Headers) of
- false -> Default;
- {value, Val} -> Val
- end.
-
-extract_headers(Content) ->
- #content{properties = #'P_basic'{headers = Headers}} =
- rabbit_binary_parser:ensure_content_decoded(Content),
- Headers.
-
-extract_timestamp(Content) ->
- #content{properties = #'P_basic'{timestamp = Timestamp}} =
- rabbit_binary_parser:ensure_content_decoded(Content),
- Timestamp.
-
-map_headers(F, Content) ->
- Content1 = rabbit_binary_parser:ensure_content_decoded(Content),
- #content{properties = #'P_basic'{headers = Headers} = Props} = Content1,
- Headers1 = F(Headers),
- rabbit_binary_generator:clear_encoded_content(
- Content1#content{properties = Props#'P_basic'{headers = Headers1}}).
-
-indexof(L, Element) -> indexof(L, Element, 1).
-
-indexof([], _Element, _N) -> 0;
-indexof([Element | _Rest], Element, N) -> N;
-indexof([_ | Rest], Element, N) -> indexof(Rest, Element, N + 1).
-
-is_message_persistent(#content{properties = #'P_basic'{
- delivery_mode = Mode}}) ->
- case Mode of
- 1 -> false;
- 2 -> true;
- undefined -> false;
- Other -> throw({error, {delivery_mode_unknown, Other}})
- end.
-
-%% Extract CC routes from headers
-header_routes(undefined) ->
- [];
-header_routes(HeadersTable) ->
- lists:append(
- [case rabbit_misc:table_lookup(HeadersTable, HeaderKey) of
- {array, Routes} -> [Route || {longstr, Route} <- Routes];
- undefined -> [];
- {Type, _Val} -> throw({error, {unacceptable_type_in_header,
- binary_to_list(HeaderKey), Type}})
- end || HeaderKey <- ?ROUTING_HEADERS]).
-
-parse_expiration(#'P_basic'{expiration = undefined}) ->
- {ok, undefined};
-parse_expiration(#'P_basic'{expiration = Expiration}) ->
- case string:to_integer(binary_to_list(Expiration)) of
- {error, no_integer} = E ->
- E;
- {N, ""} ->
- case rabbit_misc:check_expiry(N) of
- ok -> {ok, N};
- E = {error, _} -> E
- end;
- {_, S} ->
- {error, {leftover_string, S}}
- end.
-
-%% Some processes (channel, writer) can get huge amounts of binary
-%% garbage when processing huge messages at high speed (since we only
-%% do enough reductions to GC every few hundred messages, and if each
-%% message is 1MB then that's ugly). So count how many bytes of
-%% message we have processed, and force a GC every so often.
-maybe_gc_large_msg(Content) ->
- Size = msg_size(Content),
- Current = case get(msg_size_for_gc) of
- undefined -> 0;
- C -> C
- end,
- New = Current + Size,
- put(msg_size_for_gc, case New > 1000000 of
- true -> erlang:garbage_collect(),
- 0;
- false -> New
- end),
- Size.
-
-msg_size(#content{payload_fragments_rev = PFR}) -> iolist_size(PFR);
-msg_size(#basic_message{content = Content}) -> msg_size(Content).
diff --git a/src/rabbit_binary_generator.erl b/src/rabbit_binary_generator.erl
deleted file mode 100644
index 34f2d601aa..0000000000
--- a/src/rabbit_binary_generator.erl
+++ /dev/null
@@ -1,241 +0,0 @@
-%% The contents of this file are subject to the Mozilla Public License
-%% Version 1.1 (the "License"); you may not use this file except in
-%% compliance with the License. You may obtain a copy of the License
-%% at http://www.mozilla.org/MPL/
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and
-%% limitations under the License.
-%%
-%% The Original Code is RabbitMQ.
-%%
-%% The Initial Developer of the Original Code is GoPivotal, Inc.
-%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved.
-%%
-
--module(rabbit_binary_generator).
--include("rabbit_framing.hrl").
--include("rabbit.hrl").
-
--export([build_simple_method_frame/3,
- build_simple_content_frames/4,
- build_heartbeat_frame/0]).
--export([generate_table/1]).
--export([check_empty_frame_size/0]).
--export([ensure_content_encoded/2, clear_encoded_content/1]).
--export([map_exception/3]).
-
-%%----------------------------------------------------------------------------
-
--ifdef(use_specs).
-
--type(frame() :: [binary()]).
-
--spec(build_simple_method_frame/3 ::
- (rabbit_channel:channel_number(), rabbit_framing:amqp_method_record(),
- rabbit_types:protocol())
- -> frame()).
--spec(build_simple_content_frames/4 ::
- (rabbit_channel:channel_number(), rabbit_types:content(),
- non_neg_integer(), rabbit_types:protocol())
- -> [frame()]).
--spec(build_heartbeat_frame/0 :: () -> frame()).
--spec(generate_table/1 :: (rabbit_framing:amqp_table()) -> binary()).
--spec(check_empty_frame_size/0 :: () -> 'ok').
--spec(ensure_content_encoded/2 ::
- (rabbit_types:content(), rabbit_types:protocol()) ->
- rabbit_types:encoded_content()).
--spec(clear_encoded_content/1 ::
- (rabbit_types:content()) -> rabbit_types:unencoded_content()).
--spec(map_exception/3 :: (rabbit_channel:channel_number(),
- rabbit_types:amqp_error() | any(),
- rabbit_types:protocol()) ->
- {rabbit_channel:channel_number(),
- rabbit_framing:amqp_method_record()}).
-
--endif.
-
-%%----------------------------------------------------------------------------
-
-build_simple_method_frame(ChannelInt, MethodRecord, Protocol) ->
- MethodFields = Protocol:encode_method_fields(MethodRecord),
- MethodName = rabbit_misc:method_record_type(MethodRecord),
- {ClassId, MethodId} = Protocol:method_id(MethodName),
- create_frame(1, ChannelInt, [<<ClassId:16, MethodId:16>>, MethodFields]).
-
-build_simple_content_frames(ChannelInt, Content, FrameMax, Protocol) ->
- #content{class_id = ClassId,
- properties_bin = ContentPropertiesBin,
- payload_fragments_rev = PayloadFragmentsRev} =
- ensure_content_encoded(Content, Protocol),
- {BodySize, ContentFrames} =
- build_content_frames(PayloadFragmentsRev, FrameMax, ChannelInt),
- HeaderFrame = create_frame(2, ChannelInt,
- [<<ClassId:16, 0:16, BodySize:64>>,
- ContentPropertiesBin]),
- [HeaderFrame | ContentFrames].
-
-build_content_frames(FragsRev, FrameMax, ChannelInt) ->
- BodyPayloadMax = if FrameMax == 0 -> iolist_size(FragsRev);
- true -> FrameMax - ?EMPTY_FRAME_SIZE
- end,
- build_content_frames(0, [], BodyPayloadMax, [],
- lists:reverse(FragsRev), BodyPayloadMax, ChannelInt).
-
-build_content_frames(SizeAcc, FramesAcc, _FragSizeRem, [],
- [], _BodyPayloadMax, _ChannelInt) ->
- {SizeAcc, lists:reverse(FramesAcc)};
-build_content_frames(SizeAcc, FramesAcc, FragSizeRem, FragAcc,
- Frags, BodyPayloadMax, ChannelInt)
- when FragSizeRem == 0 orelse Frags == [] ->
- Frame = create_frame(3, ChannelInt, lists:reverse(FragAcc)),
- FrameSize = BodyPayloadMax - FragSizeRem,
- build_content_frames(SizeAcc + FrameSize, [Frame | FramesAcc],
- BodyPayloadMax, [], Frags, BodyPayloadMax, ChannelInt);
-build_content_frames(SizeAcc, FramesAcc, FragSizeRem, FragAcc,
- [Frag | Frags], BodyPayloadMax, ChannelInt) ->
- Size = size(Frag),
- {NewFragSizeRem, NewFragAcc, NewFrags} =
- if Size == 0 -> {FragSizeRem, FragAcc, Frags};
- Size =< FragSizeRem -> {FragSizeRem - Size, [Frag | FragAcc], Frags};
- true -> <<Head:FragSizeRem/binary, Tail/binary>> =
- Frag,
- {0, [Head | FragAcc], [Tail | Frags]}
- end,
- build_content_frames(SizeAcc, FramesAcc, NewFragSizeRem, NewFragAcc,
- NewFrags, BodyPayloadMax, ChannelInt).
-
-build_heartbeat_frame() ->
- create_frame(?FRAME_HEARTBEAT, 0, <<>>).
-
-create_frame(TypeInt, ChannelInt, Payload) ->
- [<<TypeInt:8, ChannelInt:16, (iolist_size(Payload)):32>>, Payload,
- ?FRAME_END].
-
-%% table_field_to_binary supports the AMQP 0-8/0-9 standard types, S,
-%% I, D, T and F, as well as the QPid extensions b, d, f, l, s, t, x,
-%% and V.
-table_field_to_binary({FName, T, V}) ->
- [short_string_to_binary(FName) | field_value_to_binary(T, V)].
-
-field_value_to_binary(longstr, V) -> [$S | long_string_to_binary(V)];
-field_value_to_binary(signedint, V) -> [$I, <<V:32/signed>>];
-field_value_to_binary(decimal, V) -> {Before, After} = V,
- [$D, Before, <<After:32>>];
-field_value_to_binary(timestamp, V) -> [$T, <<V:64>>];
-field_value_to_binary(table, V) -> [$F | table_to_binary(V)];
-field_value_to_binary(array, V) -> [$A | array_to_binary(V)];
-field_value_to_binary(byte, V) -> [$b, <<V:8/signed>>];
-field_value_to_binary(double, V) -> [$d, <<V:64/float>>];
-field_value_to_binary(float, V) -> [$f, <<V:32/float>>];
-field_value_to_binary(long, V) -> [$l, <<V:64/signed>>];
-field_value_to_binary(short, V) -> [$s, <<V:16/signed>>];
-field_value_to_binary(bool, V) -> [$t, if V -> 1; true -> 0 end];
-field_value_to_binary(binary, V) -> [$x | long_string_to_binary(V)];
-field_value_to_binary(void, _V) -> [$V].
-
-table_to_binary(Table) when is_list(Table) ->
- BinTable = generate_table_iolist(Table),
- [<<(iolist_size(BinTable)):32>> | BinTable].
-
-array_to_binary(Array) when is_list(Array) ->
- BinArray = generate_array_iolist(Array),
- [<<(iolist_size(BinArray)):32>> | BinArray].
-
-generate_table(Table) when is_list(Table) ->
- list_to_binary(generate_table_iolist(Table)).
-
-generate_table_iolist(Table) ->
- lists:map(fun table_field_to_binary/1, Table).
-
-generate_array_iolist(Array) ->
- lists:map(fun ({T, V}) -> field_value_to_binary(T, V) end, Array).
-
-short_string_to_binary(String) ->
- Len = string_length(String),
- if Len < 256 -> [<<Len:8>>, String];
- true -> exit(content_properties_shortstr_overflow)
- end.
-
-long_string_to_binary(String) ->
- Len = string_length(String),
- [<<Len:32>>, String].
-
-string_length(String) when is_binary(String) -> size(String);
-string_length(String) -> length(String).
-
-check_empty_frame_size() ->
- %% Intended to ensure that EMPTY_FRAME_SIZE is defined correctly.
- case iolist_size(create_frame(?FRAME_BODY, 0, <<>>)) of
- ?EMPTY_FRAME_SIZE -> ok;
- ComputedSize -> exit({incorrect_empty_frame_size,
- ComputedSize, ?EMPTY_FRAME_SIZE})
- end.
-
-ensure_content_encoded(Content = #content{properties_bin = PropBin,
- protocol = Protocol}, Protocol)
- when PropBin =/= none ->
- Content;
-ensure_content_encoded(Content = #content{properties = none,
- properties_bin = PropBin,
- protocol = Protocol}, Protocol1)
- when PropBin =/= none ->
- Props = Protocol:decode_properties(Content#content.class_id, PropBin),
- Content#content{properties = Props,
- properties_bin = Protocol1:encode_properties(Props),
- protocol = Protocol1};
-ensure_content_encoded(Content = #content{properties = Props}, Protocol)
- when Props =/= none ->
- Content#content{properties_bin = Protocol:encode_properties(Props),
- protocol = Protocol}.
-
-clear_encoded_content(Content = #content{properties_bin = none,
- protocol = none}) ->
- Content;
-clear_encoded_content(Content = #content{properties = none}) ->
- %% Only clear when we can rebuild the properties_bin later in
- %% accordance to the content record definition comment - maximum
- %% one of properties and properties_bin can be 'none'
- Content;
-clear_encoded_content(Content = #content{}) ->
- Content#content{properties_bin = none, protocol = none}.
-
-%% NB: this function is also used by the Erlang client
-map_exception(Channel, Reason, Protocol) ->
- {SuggestedClose, ReplyCode, ReplyText, FailedMethod} =
- lookup_amqp_exception(Reason, Protocol),
- {ClassId, MethodId} = case FailedMethod of
- {_, _} -> FailedMethod;
- none -> {0, 0};
- _ -> Protocol:method_id(FailedMethod)
- end,
- case SuggestedClose orelse (Channel == 0) of
- true -> {0, #'connection.close'{reply_code = ReplyCode,
- reply_text = ReplyText,
- class_id = ClassId,
- method_id = MethodId}};
- false -> {Channel, #'channel.close'{reply_code = ReplyCode,
- reply_text = ReplyText,
- class_id = ClassId,
- method_id = MethodId}}
- end.
-
-lookup_amqp_exception(#amqp_error{name = Name,
- explanation = Expl,
- method = Method},
- Protocol) ->
- {ShouldClose, Code, Text} = Protocol:lookup_amqp_exception(Name),
- ExplBin = amqp_exception_explanation(Text, Expl),
- {ShouldClose, Code, ExplBin, Method};
-lookup_amqp_exception(Other, Protocol) ->
- rabbit_log:warning("Non-AMQP exit reason '~p'~n", [Other]),
- {ShouldClose, Code, Text} = Protocol:lookup_amqp_exception(internal_error),
- {ShouldClose, Code, Text, none}.
-
-amqp_exception_explanation(Text, Expl) ->
- ExplBin = list_to_binary(Expl),
- CompleteTextBin = <<Text/binary, " - ", ExplBin/binary>>,
- if size(CompleteTextBin) > 255 -> <<CompleteTextBin:252/binary, "...">>;
- true -> CompleteTextBin
- end.
diff --git a/src/rabbit_binary_parser.erl b/src/rabbit_binary_parser.erl
deleted file mode 100644
index 8b3bf3e6f5..0000000000
--- a/src/rabbit_binary_parser.erl
+++ /dev/null
@@ -1,161 +0,0 @@
-%% The contents of this file are subject to the Mozilla Public License
-%% Version 1.1 (the "License"); you may not use this file except in
-%% compliance with the License. You may obtain a copy of the License
-%% at http://www.mozilla.org/MPL/
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and
-%% limitations under the License.
-%%
-%% The Original Code is RabbitMQ.
-%%
-%% The Initial Developer of the Original Code is GoPivotal, Inc.
-%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved.
-%%
-
--module(rabbit_binary_parser).
-
--include("rabbit.hrl").
-
--export([parse_table/1]).
--export([ensure_content_decoded/1, clear_decoded_content/1]).
--export([validate_utf8/1, assert_utf8/1]).
-
-%%----------------------------------------------------------------------------
-
--ifdef(use_specs).
-
--spec(parse_table/1 :: (binary()) -> rabbit_framing:amqp_table()).
--spec(ensure_content_decoded/1 ::
- (rabbit_types:content()) -> rabbit_types:decoded_content()).
--spec(clear_decoded_content/1 ::
- (rabbit_types:content()) -> rabbit_types:undecoded_content()).
--spec(validate_utf8/1 :: (binary()) -> 'ok' | 'error').
--spec(assert_utf8/1 :: (binary()) -> 'ok').
-
--endif.
-
-%%----------------------------------------------------------------------------
-
-%% parse_table supports the AMQP 0-8/0-9 standard types, S, I, D, T
-%% and F, as well as the QPid extensions b, d, f, l, s, t, x, and V.
-
--define(SIMPLE_PARSE_TABLE(BType, Pattern, RType),
- parse_table(<<NLen:8/unsigned, NameString:NLen/binary,
- BType, Pattern, Rest/binary>>) ->
- [{NameString, RType, Value} | parse_table(Rest)]).
-
-%% Note that we try to put these in approximately the order we expect
-%% to hit them, that's why the empty binary is half way through.
-
-parse_table(<<NLen:8/unsigned, NameString:NLen/binary,
- $S, VLen:32/unsigned, Value:VLen/binary, Rest/binary>>) ->
- [{NameString, longstr, Value} | parse_table(Rest)];
-
-?SIMPLE_PARSE_TABLE($I, Value:32/signed, signedint);
-?SIMPLE_PARSE_TABLE($T, Value:64/unsigned, timestamp);
-
-parse_table(<<>>) ->
- [];
-
-?SIMPLE_PARSE_TABLE($b, Value:8/signed, byte);
-?SIMPLE_PARSE_TABLE($d, Value:64/float, double);
-?SIMPLE_PARSE_TABLE($f, Value:32/float, float);
-?SIMPLE_PARSE_TABLE($l, Value:64/signed, long);
-?SIMPLE_PARSE_TABLE($s, Value:16/signed, short);
-
-parse_table(<<NLen:8/unsigned, NameString:NLen/binary,
- $t, Value:8/unsigned, Rest/binary>>) ->
- [{NameString, bool, (Value /= 0)} | parse_table(Rest)];
-
-parse_table(<<NLen:8/unsigned, NameString:NLen/binary,
- $D, Before:8/unsigned, After:32/unsigned, Rest/binary>>) ->
- [{NameString, decimal, {Before, After}} | parse_table(Rest)];
-
-parse_table(<<NLen:8/unsigned, NameString:NLen/binary,
- $F, VLen:32/unsigned, Value:VLen/binary, Rest/binary>>) ->
- [{NameString, table, parse_table(Value)} | parse_table(Rest)];
-
-parse_table(<<NLen:8/unsigned, NameString:NLen/binary,
- $A, VLen:32/unsigned, Value:VLen/binary, Rest/binary>>) ->
- [{NameString, array, parse_array(Value)} | parse_table(Rest)];
-
-parse_table(<<NLen:8/unsigned, NameString:NLen/binary,
- $x, VLen:32/unsigned, Value:VLen/binary, Rest/binary>>) ->
- [{NameString, binary, Value} | parse_table(Rest)];
-
-parse_table(<<NLen:8/unsigned, NameString:NLen/binary,
- $V, Rest/binary>>) ->
- [{NameString, void, undefined} | parse_table(Rest)].
-
--define(SIMPLE_PARSE_ARRAY(BType, Pattern, RType),
- parse_array(<<BType, Pattern, Rest/binary>>) ->
- [{RType, Value} | parse_array(Rest)]).
-
-parse_array(<<$S, VLen:32/unsigned, Value:VLen/binary, Rest/binary>>) ->
- [{longstr, Value} | parse_array(Rest)];
-
-?SIMPLE_PARSE_ARRAY($I, Value:32/signed, signedint);
-?SIMPLE_PARSE_ARRAY($T, Value:64/unsigned, timestamp);
-
-parse_array(<<>>) ->
- [];
-
-?SIMPLE_PARSE_ARRAY($b, Value:8/signed, byte);
-?SIMPLE_PARSE_ARRAY($d, Value:64/float, double);
-?SIMPLE_PARSE_ARRAY($f, Value:32/float, float);
-?SIMPLE_PARSE_ARRAY($l, Value:64/signed, long);
-?SIMPLE_PARSE_ARRAY($s, Value:16/signed, short);
-
-parse_array(<<$t, Value:8/unsigned, Rest/binary>>) ->
- [{bool, (Value /= 0)} | parse_array(Rest)];
-
-parse_array(<<$D, Before:8/unsigned, After:32/unsigned, Rest/binary>>) ->
- [{decimal, {Before, After}} | parse_array(Rest)];
-
-parse_array(<<$F, VLen:32/unsigned, Value:VLen/binary, Rest/binary>>) ->
- [{table, parse_table(Value)} | parse_array(Rest)];
-
-parse_array(<<$A, VLen:32/unsigned, Value:VLen/binary, Rest/binary>>) ->
- [{array, parse_array(Value)} | parse_array(Rest)];
-
-parse_array(<<$x, VLen:32/unsigned, Value:VLen/binary, Rest/binary>>) ->
- [{binary, Value} | parse_array(Rest)];
-
-parse_array(<<$V, Rest/binary>>) ->
- [{void, undefined} | parse_array(Rest)].
-
-ensure_content_decoded(Content = #content{properties = Props})
- when Props =/= none ->
- Content;
-ensure_content_decoded(Content = #content{properties_bin = PropBin,
- protocol = Protocol})
- when PropBin =/= none ->
- Content#content{properties = Protocol:decode_properties(
- Content#content.class_id, PropBin)}.
-
-clear_decoded_content(Content = #content{properties = none}) ->
- Content;
-clear_decoded_content(Content = #content{properties_bin = none}) ->
- %% Only clear when we can rebuild the properties later in
- %% accordance to the content record definition comment - maximum
- %% one of properties and properties_bin can be 'none'
- Content;
-clear_decoded_content(Content = #content{}) ->
- Content#content{properties = none}.
-
-assert_utf8(B) ->
- case validate_utf8(B) of
- ok -> ok;
- error -> rabbit_misc:protocol_error(
- frame_error, "Malformed UTF-8 in shortstr", [])
- end.
-
-validate_utf8(Bin) ->
- try
- xmerl_ucs:from_utf8(Bin),
- ok
- catch exit:{ucs, _} ->
- error
- end.
diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl
deleted file mode 100644
index f8ed9cae9f..0000000000
--- a/src/rabbit_channel.erl
+++ /dev/null
@@ -1,2017 +0,0 @@
-%% The contents of this file are subject to the Mozilla Public License
-%% Version 1.1 (the "License"); you may not use this file except in
-%% compliance with the License. You may obtain a copy of the License
-%% at http://www.mozilla.org/MPL/
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and
-%% limitations under the License.
-%%
-%% The Original Code is RabbitMQ.
-%%
-%% The Initial Developer of the Original Code is GoPivotal, Inc.
-%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved.
-%%
-
--module(rabbit_channel).
-
-%% rabbit_channel processes represent an AMQP 0-9-1 channels.
-%%
-%% Connections parse protocol frames coming from clients and
-%% dispatch them to channel processes.
-%% Channels are responsible for implementing the logic behind
-%% the various protocol methods, involving other processes as
-%% needed:
-%%
-%% * Routing messages (using functions in various exchange type
-%% modules) to queue processes.
-%% * Managing queues, exchanges, and bindings.
-%% * Keeping track of consumers
-%% * Keeping track of unacknowledged deliveries to consumers
-%% * Keeping track of publisher confirms
-%% * Keeping track of mandatory message routing confirmations
-%% and returns
-%% * Transaction management
-%% * Authorisation (enforcing permissions)
-%% * Publishing trace events if tracing is enabled
-%%
-%% Every channel has a number of dependent processes:
-%%
-%% * A writer which is responsible for sending frames to clients.
-%% * A limiter which controls how many messages can be delivered
-%% to consumers according to active QoS prefetch and internal
-%% flow control logic.
-%%
-%% Channels are also aware of their connection's queue collector.
-%% When a queue is declared as exclusive on a channel, the channel
-%% will notify queue collector of that queue.
-
--include("rabbit_framing.hrl").
--include("rabbit.hrl").
-
--behaviour(gen_server2).
-
--export([start_link/11, do/2, do/3, do_flow/3, flush/1, shutdown/1]).
--export([send_command/2, deliver/4, deliver_reply/2,
- send_credit_reply/2, send_drained/2]).
--export([list/0, info_keys/0, info/1, info/2, info_all/0, info_all/1]).
--export([refresh_config_local/0, ready_for_close/1]).
--export([force_event_refresh/1]).
-
--export([init/1, terminate/2, code_change/3, handle_call/3, handle_cast/2,
- handle_info/2, handle_pre_hibernate/1, prioritise_call/4,
- prioritise_cast/3, prioritise_info/3, format_message_queue/2]).
-%% Internal
--export([list_local/0, deliver_reply_local/3]).
--export([get_vhost/1, get_user/1]).
-
--record(ch, {
- %% starting | running | flow | closing
- state,
- %% same as reader's protocol. Used when instantiating
- %% (protocol) exceptions.
- protocol,
- %% channel number
- channel,
- %% reader process
- reader_pid,
- %% writer process
- writer_pid,
- %%
- conn_pid,
- %% same as reader's name, see #v1.name
- %% in rabbit_reader
- conn_name,
- %% limiter pid, see rabbit_limiter
- limiter,
- %% none | {Msgs, Acks} | committing | failed |
- tx,
- %% (consumer) delivery tag sequence
- next_tag,
- %% messages pending consumer acknowledgement
- unacked_message_q,
- %% same as #v1.user in the reader, used in
- %% authorisation checks
- user,
- %% same as #v1.user in the reader
- virtual_host,
- %% when queue.bind's queue field is empty,
- %% this name will be used instead
- most_recently_declared_queue,
- %% a dictionary of queue pid to queue name
- queue_names,
- %% queue processes are monitored to update
- %% queue names
- queue_monitors,
- %% a dictionary of consumer tags to
- %% consumer details: #amqqueue record, acknowledgement mode,
- %% consumer exclusivity, etc
- consumer_mapping,
- %% a dictionary of queue pids to consumer tag lists
- queue_consumers,
- %% a set of pids of queues that have unacknowledged
- %% deliveries
- delivering_queues,
- %% when a queue is declared as exclusive, queue
- %% collector must be notified.
- %% see rabbit_queue_collector for more info.
- queue_collector_pid,
- %% timer used to emit statistics
- stats_timer,
- %% are publisher confirms enabled for this channel?
- confirm_enabled,
- %% publisher confirm delivery tag sequence
- publish_seqno,
- %% a dtree used to track unconfirmed
- %% (to publishers) messages
- unconfirmed,
- %% a list of tags for published messages that were
- %% delivered but are yet to be confirmed to the client
- confirmed,
- %% a dtree used to track oustanding notifications
- %% for messages published as mandatory
- mandatory,
- %% same as capabilities in the reader
- capabilities,
- %% tracing exchange resource if tracing is enabled,
- %% 'none' otherwise
- trace_state,
- consumer_prefetch,
- %% used by "one shot RPC" (amq.
- reply_consumer,
- %% flow | noflow, see rabbitmq-server#114
- delivery_flow,
- interceptor_state
-}).
-
-
--define(MAX_PERMISSION_CACHE_SIZE, 12).
-
--define(STATISTICS_KEYS,
- [pid,
- transactional,
- confirm,
- consumer_count,
- messages_unacknowledged,
- messages_unconfirmed,
- messages_uncommitted,
- acks_uncommitted,
- prefetch_count,
- global_prefetch_count,
- state]).
-
--define(CREATION_EVENT_KEYS,
- [pid,
- name,
- connection,
- number,
- user,
- vhost]).
-
--define(INFO_KEYS, ?CREATION_EVENT_KEYS ++ ?STATISTICS_KEYS -- [pid]).
-
--define(INCR_STATS(Incs, Measure, State),
- case rabbit_event:stats_level(State, #ch.stats_timer) of
- fine -> incr_stats(Incs, Measure);
- _ -> ok
- end).
-
-%%----------------------------------------------------------------------------
-
--ifdef(use_specs).
-
--export_type([channel_number/0]).
-
--type(channel_number() :: non_neg_integer()).
-
--export_type([channel/0]).
-
--type(channel() :: #ch{}).
-
--spec(start_link/11 ::
- (channel_number(), pid(), pid(), pid(), string(),
- rabbit_types:protocol(), rabbit_types:user(), rabbit_types:vhost(),
- rabbit_framing:amqp_table(), pid(), pid()) ->
- rabbit_types:ok_pid_or_error()).
--spec(do/2 :: (pid(), rabbit_framing:amqp_method_record()) -> 'ok').
--spec(do/3 :: (pid(), rabbit_framing:amqp_method_record(),
- rabbit_types:maybe(rabbit_types:content())) -> 'ok').
--spec(do_flow/3 :: (pid(), rabbit_framing:amqp_method_record(),
- rabbit_types:maybe(rabbit_types:content())) -> 'ok').
--spec(flush/1 :: (pid()) -> 'ok').
--spec(shutdown/1 :: (pid()) -> 'ok').
--spec(send_command/2 :: (pid(), rabbit_framing:amqp_method_record()) -> 'ok').
--spec(deliver/4 ::
- (pid(), rabbit_types:ctag(), boolean(), rabbit_amqqueue:qmsg())
- -> 'ok').
--spec(deliver_reply/2 :: (binary(), rabbit_types:delivery()) -> 'ok').
--spec(deliver_reply_local/3 ::
- (pid(), binary(), rabbit_types:delivery()) -> 'ok').
--spec(send_credit_reply/2 :: (pid(), non_neg_integer()) -> 'ok').
--spec(send_drained/2 :: (pid(), [{rabbit_types:ctag(), non_neg_integer()}])
- -> 'ok').
--spec(list/0 :: () -> [pid()]).
--spec(list_local/0 :: () -> [pid()]).
--spec(info_keys/0 :: () -> rabbit_types:info_keys()).
--spec(info/1 :: (pid()) -> rabbit_types:infos()).
--spec(info/2 :: (pid(), rabbit_types:info_keys()) -> rabbit_types:infos()).
--spec(info_all/0 :: () -> [rabbit_types:infos()]).
--spec(info_all/1 :: (rabbit_types:info_keys()) -> [rabbit_types:infos()]).
--spec(refresh_config_local/0 :: () -> 'ok').
--spec(ready_for_close/1 :: (pid()) -> 'ok').
--spec(force_event_refresh/1 :: (reference()) -> 'ok').
-
--endif.
-
-%%----------------------------------------------------------------------------
-
-start_link(Channel, ReaderPid, WriterPid, ConnPid, ConnName, Protocol, User,
- VHost, Capabilities, CollectorPid, Limiter) ->
- gen_server2:start_link(
- ?MODULE, [Channel, ReaderPid, WriterPid, ConnPid, ConnName, Protocol,
- User, VHost, Capabilities, CollectorPid, Limiter], []).
-
-do(Pid, Method) ->
- do(Pid, Method, none).
-
-do(Pid, Method, Content) ->
- gen_server2:cast(Pid, {method, Method, Content, noflow}).
-
-do_flow(Pid, Method, Content) ->
- %% Here we are tracking messages sent by the rabbit_reader
- %% process. We are accessing the rabbit_reader process dictionary.
- credit_flow:send(Pid),
- gen_server2:cast(Pid, {method, Method, Content, flow}).
-
-flush(Pid) ->
- gen_server2:call(Pid, flush, infinity).
-
-shutdown(Pid) ->
- gen_server2:cast(Pid, terminate).
-
-send_command(Pid, Msg) ->
- gen_server2:cast(Pid, {command, Msg}).
-
-deliver(Pid, ConsumerTag, AckRequired, Msg) ->
- gen_server2:cast(Pid, {deliver, ConsumerTag, AckRequired, Msg}).
-
-deliver_reply(<<"amq.rabbitmq.reply-to.", Rest/binary>>, Delivery) ->
- case decode_fast_reply_to(Rest) of
- {ok, Pid, Key} ->
- delegate:invoke_no_result(
- Pid, {?MODULE, deliver_reply_local, [Key, Delivery]});
- error ->
- ok
- end.
-
-%% We want to ensure people can't use this mechanism to send a message
-%% to an arbitrary process and kill it!
-deliver_reply_local(Pid, Key, Delivery) ->
- case pg_local:in_group(rabbit_channels, Pid) of
- true -> gen_server2:cast(Pid, {deliver_reply, Key, Delivery});
- false -> ok
- end.
-
-declare_fast_reply_to(<<"amq.rabbitmq.reply-to">>) ->
- exists;
-declare_fast_reply_to(<<"amq.rabbitmq.reply-to.", Rest/binary>>) ->
- case decode_fast_reply_to(Rest) of
- {ok, Pid, Key} ->
- Msg = {declare_fast_reply_to, Key},
- rabbit_misc:with_exit_handler(
- rabbit_misc:const(not_found),
- fun() -> gen_server2:call(Pid, Msg, infinity) end);
- error ->
- not_found
- end;
-declare_fast_reply_to(_) ->
- not_found.
-
-decode_fast_reply_to(Rest) ->
- case string:tokens(binary_to_list(Rest), ".") of
- [PidEnc, Key] -> Pid = binary_to_term(base64:decode(PidEnc)),
- {ok, Pid, Key};
- _ -> error
- end.
-
-send_credit_reply(Pid, Len) ->
- gen_server2:cast(Pid, {send_credit_reply, Len}).
-
-send_drained(Pid, CTagCredit) ->
- gen_server2:cast(Pid, {send_drained, CTagCredit}).
-
-list() ->
- rabbit_misc:append_rpc_all_nodes(rabbit_mnesia:cluster_nodes(running),
- rabbit_channel, list_local, []).
-
-list_local() ->
- pg_local:get_members(rabbit_channels).
-
-info_keys() -> ?INFO_KEYS.
-
-info(Pid) ->
- gen_server2:call(Pid, info, infinity).
-
-info(Pid, Items) ->
- case gen_server2:call(Pid, {info, Items}, infinity) of
- {ok, Res} -> Res;
- {error, Error} -> throw(Error)
- end.
-
-info_all() ->
- rabbit_misc:filter_exit_map(fun (C) -> info(C) end, list()).
-
-info_all(Items) ->
- rabbit_misc:filter_exit_map(fun (C) -> info(C, Items) end, list()).
-
-refresh_config_local() ->
- rabbit_misc:upmap(
- fun (C) -> gen_server2:call(C, refresh_config, infinity) end,
- list_local()),
- ok.
-
-ready_for_close(Pid) ->
- gen_server2:cast(Pid, ready_for_close).
-
-force_event_refresh(Ref) ->
- [gen_server2:cast(C, {force_event_refresh, Ref}) || C <- list()],
- ok.
-
-%%---------------------------------------------------------------------------
-
-init([Channel, ReaderPid, WriterPid, ConnPid, ConnName, Protocol, User, VHost,
- Capabilities, CollectorPid, LimiterPid]) ->
- process_flag(trap_exit, true),
- ?store_proc_name({ConnName, Channel}),
- ok = pg_local:join(rabbit_channels, self()),
- Flow = case rabbit_misc:get_env(rabbit, mirroring_flow_control, true) of
- true -> flow;
- false -> noflow
- end,
- State = #ch{state = starting,
- protocol = Protocol,
- channel = Channel,
- reader_pid = ReaderPid,
- writer_pid = WriterPid,
- conn_pid = ConnPid,
- conn_name = ConnName,
- limiter = rabbit_limiter:new(LimiterPid),
- tx = none,
- next_tag = 1,
- unacked_message_q = queue:new(),
- user = User,
- virtual_host = VHost,
- most_recently_declared_queue = <<>>,
- queue_names = dict:new(),
- queue_monitors = pmon:new(),
- consumer_mapping = dict:new(),
- queue_consumers = dict:new(),
- delivering_queues = sets:new(),
- queue_collector_pid = CollectorPid,
- confirm_enabled = false,
- publish_seqno = 1,
- unconfirmed = dtree:empty(),
- confirmed = [],
- mandatory = dtree:empty(),
- capabilities = Capabilities,
- trace_state = rabbit_trace:init(VHost),
- consumer_prefetch = 0,
- reply_consumer = none,
- delivery_flow = Flow,
- interceptor_state = undefined},
- State1 = State#ch{
- interceptor_state = rabbit_channel_interceptor:init(State)},
- State2 = rabbit_event:init_stats_timer(State1, #ch.stats_timer),
- rabbit_event:notify(channel_created, infos(?CREATION_EVENT_KEYS, State2)),
- rabbit_event:if_enabled(State2, #ch.stats_timer,
- fun() -> emit_stats(State2) end),
- {ok, State2, hibernate,
- {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}}.
-
-prioritise_call(Msg, _From, _Len, _State) ->
- case Msg of
- info -> 9;
- {info, _Items} -> 9;
- _ -> 0
- end.
-
-prioritise_cast(Msg, _Len, _State) ->
- case Msg of
- {confirm, _MsgSeqNos, _QPid} -> 5;
- {mandatory_received, _MsgSeqNo, _QPid} -> 5;
- _ -> 0
- end.
-
-prioritise_info(Msg, _Len, _State) ->
- case Msg of
- emit_stats -> 7;
- _ -> 0
- end.
-
-handle_call(flush, _From, State) ->
- reply(ok, State);
-
-handle_call(info, _From, State) ->
- reply(infos(?INFO_KEYS, State), State);
-
-handle_call({info, Items}, _From, State) ->
- try
- reply({ok, infos(Items, State)}, State)
- catch Error -> reply({error, Error}, State)
- end;
-
-handle_call(refresh_config, _From, State = #ch{virtual_host = VHost}) ->
- reply(ok, State#ch{trace_state = rabbit_trace:init(VHost)});
-
-handle_call({declare_fast_reply_to, Key}, _From,
- State = #ch{reply_consumer = Consumer}) ->
- reply(case Consumer of
- {_, _, Key} -> exists;
- _ -> not_found
- end, State);
-
-handle_call(_Request, _From, State) ->
- noreply(State).
-
-handle_cast({method, Method, Content, Flow},
- State = #ch{reader_pid = Reader,
- interceptor_state = IState}) ->
- case Flow of
- %% We are going to process a message from the rabbit_reader
- %% process, so here we ack it. In this case we are accessing
- %% the rabbit_channel process dictionary.
- flow -> credit_flow:ack(Reader);
- noflow -> ok
- end,
-
- try handle_method(rabbit_channel_interceptor:intercept_in(
- expand_shortcuts(Method, State), Content, IState),
- State) of
- {reply, Reply, NewState} ->
- ok = send(Reply, NewState),
- noreply(NewState);
- {noreply, NewState} ->
- noreply(NewState);
- stop ->
- {stop, normal, State}
- catch
- exit:Reason = #amqp_error{} ->
- MethodName = rabbit_misc:method_record_type(Method),
- handle_exception(Reason#amqp_error{method = MethodName}, State);
- _:Reason ->
- {stop, {Reason, erlang:get_stacktrace()}, State}
- end;
-
-handle_cast(ready_for_close, State = #ch{state = closing,
- writer_pid = WriterPid}) ->
- ok = rabbit_writer:send_command_sync(WriterPid, #'channel.close_ok'{}),
- {stop, normal, State};
-
-handle_cast(terminate, State = #ch{writer_pid = WriterPid}) ->
- ok = rabbit_writer:flush(WriterPid),
- {stop, normal, State};
-
-handle_cast({command, #'basic.consume_ok'{consumer_tag = CTag} = Msg}, State) ->
- ok = send(Msg, State),
- noreply(consumer_monitor(CTag, State));
-
-handle_cast({command, Msg}, State) ->
- ok = send(Msg, State),
- noreply(State);
-
-handle_cast({deliver, _CTag, _AckReq, _Msg}, State = #ch{state = closing}) ->
- noreply(State);
-handle_cast({deliver, ConsumerTag, AckRequired,
- Msg = {_QName, QPid, _MsgId, Redelivered,
- #basic_message{exchange_name = ExchangeName,
- routing_keys = [RoutingKey | _CcRoutes],
- content = Content}}},
- State = #ch{writer_pid = WriterPid,
- next_tag = DeliveryTag}) ->
- ok = rabbit_writer:send_command_and_notify(
- WriterPid, QPid, self(),
- #'basic.deliver'{consumer_tag = ConsumerTag,
- delivery_tag = DeliveryTag,
- redelivered = Redelivered,
- exchange = ExchangeName#resource.name,
- routing_key = RoutingKey},
- Content),
- rabbit_basic:maybe_gc_large_msg(Content),
- noreply(record_sent(ConsumerTag, AckRequired, Msg, State));
-
-handle_cast({deliver_reply, _K, _Del}, State = #ch{state = closing}) ->
- noreply(State);
-handle_cast({deliver_reply, _K, _Del}, State = #ch{reply_consumer = none}) ->
- noreply(State);
-handle_cast({deliver_reply, Key, #delivery{message =
- #basic_message{exchange_name = ExchangeName,
- routing_keys = [RoutingKey | _CcRoutes],
- content = Content}}},
- State = #ch{writer_pid = WriterPid,
- next_tag = DeliveryTag,
- reply_consumer = {ConsumerTag, _Suffix, Key}}) ->
- ok = rabbit_writer:send_command(
- WriterPid,
- #'basic.deliver'{consumer_tag = ConsumerTag,
- delivery_tag = DeliveryTag,
- redelivered = false,
- exchange = ExchangeName#resource.name,
- routing_key = RoutingKey},
- Content),
- noreply(State);
-handle_cast({deliver_reply, _K1, _}, State=#ch{reply_consumer = {_, _, _K2}}) ->
- noreply(State);
-
-handle_cast({send_credit_reply, Len}, State = #ch{writer_pid = WriterPid}) ->
- ok = rabbit_writer:send_command(
- WriterPid, #'basic.credit_ok'{available = Len}),
- noreply(State);
-
-handle_cast({send_drained, CTagCredit}, State = #ch{writer_pid = WriterPid}) ->
- [ok = rabbit_writer:send_command(
- WriterPid, #'basic.credit_drained'{consumer_tag = ConsumerTag,
- credit_drained = CreditDrained})
- || {ConsumerTag, CreditDrained} <- CTagCredit],
- noreply(State);
-
-handle_cast({force_event_refresh, Ref}, State) ->
- rabbit_event:notify(channel_created, infos(?CREATION_EVENT_KEYS, State),
- Ref),
- noreply(rabbit_event:init_stats_timer(State, #ch.stats_timer));
-
-handle_cast({mandatory_received, MsgSeqNo}, State = #ch{mandatory = Mand}) ->
- %% NB: don't call noreply/1 since we don't want to send confirms.
- noreply_coalesce(State#ch{mandatory = dtree:drop(MsgSeqNo, Mand)});
-
-handle_cast({confirm, MsgSeqNos, QPid}, State = #ch{unconfirmed = UC}) ->
- {MXs, UC1} = dtree:take(MsgSeqNos, QPid, UC),
- %% NB: don't call noreply/1 since we don't want to send confirms.
- noreply_coalesce(record_confirms(MXs, State#ch{unconfirmed = UC1})).
-
-handle_info({bump_credit, Msg}, State) ->
- %% A rabbit_amqqueue_process is granting credit to our channel. If
- %% our channel was being blocked by this process, and no other
- %% process is blocking our channel, then this channel will be
- %% unblocked. This means that any credit that was deferred will be
- %% sent to rabbit_reader processs that might be blocked by this
- %% particular channel.
- credit_flow:handle_bump_msg(Msg),
- noreply(State);
-
-handle_info(timeout, State) ->
- noreply(State);
-
-handle_info(emit_stats, State) ->
- emit_stats(State),
- State1 = rabbit_event:reset_stats_timer(State, #ch.stats_timer),
- %% NB: don't call noreply/1 since we don't want to kick off the
- %% stats timer.
- {noreply, send_confirms(State1), hibernate};
-
-handle_info({'DOWN', _MRef, process, QPid, Reason}, State) ->
- State1 = handle_publishing_queue_down(QPid, Reason, State),
- State3 = handle_consuming_queue_down(QPid, State1),
- State4 = handle_delivering_queue_down(QPid, State3),
- %% A rabbit_amqqueue_process has died. If our channel was being
- %% blocked by this process, and no other process is blocking our
- %% channel, then this channel will be unblocked. This means that
- %% any credit that was deferred will be sent to the rabbit_reader
- %% processs that might be blocked by this particular channel.
- credit_flow:peer_down(QPid),
- #ch{queue_names = QNames, queue_monitors = QMons} = State4,
- case dict:find(QPid, QNames) of
- {ok, QName} -> erase_queue_stats(QName);
- error -> ok
- end,
- noreply(State4#ch{queue_names = dict:erase(QPid, QNames),
- queue_monitors = pmon:erase(QPid, QMons)});
-
-handle_info({'EXIT', _Pid, Reason}, State) ->
- {stop, Reason, State}.
-
-handle_pre_hibernate(State) ->
- ok = clear_permission_cache(),
- rabbit_event:if_enabled(
- State, #ch.stats_timer,
- fun () -> emit_stats(State,
- [{idle_since,
- time_compat:os_system_time(milli_seconds)}])
- end),
- {hibernate, rabbit_event:stop_stats_timer(State, #ch.stats_timer)}.
-
-terminate(Reason, State) ->
- {Res, _State1} = notify_queues(State),
- case Reason of
- normal -> ok = Res;
- shutdown -> ok = Res;
- {shutdown, _Term} -> ok = Res;
- _ -> ok
- end,
- pg_local:leave(rabbit_channels, self()),
- rabbit_event:if_enabled(State, #ch.stats_timer,
- fun() -> emit_stats(State) end),
- rabbit_event:notify(channel_closed, [{pid, self()}]).
-
-code_change(_OldVsn, State, _Extra) ->
- {ok, State}.
-
-format_message_queue(Opt, MQ) -> rabbit_misc:format_message_queue(Opt, MQ).
-
-%%---------------------------------------------------------------------------
-
-log(Level, Fmt, Args) -> rabbit_log:log(channel, Level, Fmt, Args).
-
-reply(Reply, NewState) -> {reply, Reply, next_state(NewState), hibernate}.
-
-noreply(NewState) -> {noreply, next_state(NewState), hibernate}.
-
-next_state(State) -> ensure_stats_timer(send_confirms(State)).
-
-noreply_coalesce(State = #ch{confirmed = C}) ->
- Timeout = case C of [] -> hibernate; _ -> 0 end,
- {noreply, ensure_stats_timer(State), Timeout}.
-
-ensure_stats_timer(State) ->
- rabbit_event:ensure_stats_timer(State, #ch.stats_timer, emit_stats).
-
-return_ok(State, true, _Msg) -> {noreply, State};
-return_ok(State, false, Msg) -> {reply, Msg, State}.
-
-ok_msg(true, _Msg) -> undefined;
-ok_msg(false, Msg) -> Msg.
-
-send(_Command, #ch{state = closing}) ->
- ok;
-send(Command, #ch{writer_pid = WriterPid}) ->
- ok = rabbit_writer:send_command(WriterPid, Command).
-
-handle_exception(Reason, State = #ch{protocol = Protocol,
- channel = Channel,
- writer_pid = WriterPid,
- reader_pid = ReaderPid,
- conn_pid = ConnPid,
- conn_name = ConnName,
- virtual_host = VHost,
- user = User}) ->
- %% something bad's happened: notify_queues may not be 'ok'
- {_Result, State1} = notify_queues(State),
- case rabbit_binary_generator:map_exception(Channel, Reason, Protocol) of
- {Channel, CloseMethod} ->
- log(error, "Channel error on connection ~p (~s, vhost: '~s',"
- " user: '~s'), channel ~p:~n~p~n",
- [ConnPid, ConnName, VHost, User#user.username,
- Channel, Reason]),
- ok = rabbit_writer:send_command(WriterPid, CloseMethod),
- {noreply, State1};
- {0, _} ->
- ReaderPid ! {channel_exit, Channel, Reason},
- {stop, normal, State1}
- end.
-
--ifdef(use_specs).
--spec(precondition_failed/1 :: (string()) -> no_return()).
--endif.
-precondition_failed(Format) -> precondition_failed(Format, []).
-
--ifdef(use_specs).
--spec(precondition_failed/2 :: (string(), [any()]) -> no_return()).
--endif.
-precondition_failed(Format, Params) ->
- rabbit_misc:protocol_error(precondition_failed, Format, Params).
-
-return_queue_declare_ok(#resource{name = ActualName},
- NoWait, MessageCount, ConsumerCount, State) ->
- return_ok(State#ch{most_recently_declared_queue = ActualName}, NoWait,
- #'queue.declare_ok'{queue = ActualName,
- message_count = MessageCount,
- consumer_count = ConsumerCount}).
-
-check_resource_access(User, Resource, Perm) ->
- V = {Resource, Perm},
- Cache = case get(permission_cache) of
- undefined -> [];
- Other -> Other
- end,
- case lists:member(V, Cache) of
- true -> ok;
- false -> ok = rabbit_access_control:check_resource_access(
- User, Resource, Perm),
- CacheTail = lists:sublist(Cache, ?MAX_PERMISSION_CACHE_SIZE-1),
- put(permission_cache, [V | CacheTail])
- end.
-
-clear_permission_cache() -> erase(permission_cache),
- ok.
-
-check_configure_permitted(Resource, #ch{user = User}) ->
- check_resource_access(User, Resource, configure).
-
-check_write_permitted(Resource, #ch{user = User}) ->
- check_resource_access(User, Resource, write).
-
-check_read_permitted(Resource, #ch{user = User}) ->
- check_resource_access(User, Resource, read).
-
-check_user_id_header(#'P_basic'{user_id = undefined}, _) ->
- ok;
-check_user_id_header(#'P_basic'{user_id = Username},
- #ch{user = #user{username = Username}}) ->
- ok;
-check_user_id_header(
- #'P_basic'{}, #ch{user = #user{authz_backends =
- [{rabbit_auth_backend_dummy, _}]}}) ->
- ok;
-check_user_id_header(#'P_basic'{user_id = Claimed},
- #ch{user = #user{username = Actual,
- tags = Tags}}) ->
- case lists:member(impersonator, Tags) of
- true -> ok;
- false -> precondition_failed(
- "user_id property set to '~s' but authenticated user was "
- "'~s'", [Claimed, Actual])
- end.
-
-check_expiration_header(Props) ->
- case rabbit_basic:parse_expiration(Props) of
- {ok, _} -> ok;
- {error, E} -> precondition_failed("invalid expiration '~s': ~p",
- [Props#'P_basic'.expiration, E])
- end.
-
-check_internal_exchange(#exchange{name = Name, internal = true}) ->
- rabbit_misc:protocol_error(access_refused,
- "cannot publish to internal ~s",
- [rabbit_misc:rs(Name)]);
-check_internal_exchange(_) ->
- ok.
-
-check_msg_size(Content) ->
- Size = rabbit_basic:maybe_gc_large_msg(Content),
- case Size > ?MAX_MSG_SIZE of
- true -> precondition_failed("message size ~B larger than max size ~B",
- [Size, ?MAX_MSG_SIZE]);
- false -> ok
- end.
-
-qbin_to_resource(QueueNameBin, State) ->
- name_to_resource(queue, QueueNameBin, State).
-
-name_to_resource(Type, NameBin, #ch{virtual_host = VHostPath}) ->
- rabbit_misc:r(VHostPath, Type, NameBin).
-
-expand_queue_name_shortcut(<<>>, #ch{most_recently_declared_queue = <<>>}) ->
- rabbit_misc:protocol_error(not_found, "no previously declared queue", []);
-expand_queue_name_shortcut(<<>>, #ch{most_recently_declared_queue = MRDQ}) ->
- MRDQ;
-expand_queue_name_shortcut(QueueNameBin, _) ->
- QueueNameBin.
-
-expand_routing_key_shortcut(<<>>, <<>>,
- #ch{most_recently_declared_queue = <<>>}) ->
- rabbit_misc:protocol_error(not_found, "no previously declared queue", []);
-expand_routing_key_shortcut(<<>>, <<>>,
- #ch{most_recently_declared_queue = MRDQ}) ->
- MRDQ;
-expand_routing_key_shortcut(_QueueNameBin, RoutingKey, _State) ->
- RoutingKey.
-
-expand_shortcuts(#'basic.get' {queue = Q} = M, State) ->
- M#'basic.get' {queue = expand_queue_name_shortcut(Q, State)};
-expand_shortcuts(#'basic.consume'{queue = Q} = M, State) ->
- M#'basic.consume'{queue = expand_queue_name_shortcut(Q, State)};
-expand_shortcuts(#'queue.delete' {queue = Q} = M, State) ->
- M#'queue.delete' {queue = expand_queue_name_shortcut(Q, State)};
-expand_shortcuts(#'queue.purge' {queue = Q} = M, State) ->
- M#'queue.purge' {queue = expand_queue_name_shortcut(Q, State)};
-expand_shortcuts(#'queue.bind' {queue = Q, routing_key = K} = M, State) ->
- M#'queue.bind' {queue = expand_queue_name_shortcut(Q, State),
- routing_key = expand_routing_key_shortcut(Q, K, State)};
-expand_shortcuts(#'queue.unbind' {queue = Q, routing_key = K} = M, State) ->
- M#'queue.unbind' {queue = expand_queue_name_shortcut(Q, State),
- routing_key = expand_routing_key_shortcut(Q, K, State)};
-expand_shortcuts(M, _State) ->
- M.
-
-check_not_default_exchange(#resource{kind = exchange, name = <<"">>}) ->
- rabbit_misc:protocol_error(
- access_refused, "operation not permitted on the default exchange", []);
-check_not_default_exchange(_) ->
- ok.
-
-check_exchange_deletion(XName = #resource{name = <<"amq.", _/binary>>,
- kind = exchange}) ->
- rabbit_misc:protocol_error(
- access_refused, "deletion of system ~s not allowed",
- [rabbit_misc:rs(XName)]);
-check_exchange_deletion(_) ->
- ok.
-
-%% check that an exchange/queue name does not contain the reserved
-%% "amq." prefix.
-%%
-%% As per the AMQP 0-9-1 spec, the exclusion of "amq." prefixed names
-%% only applies on actual creation, and not in the cases where the
-%% entity already exists or passive=true.
-%%
-%% NB: We deliberately do not enforce the other constraints on names
-%% required by the spec.
-check_name(Kind, NameBin = <<"amq.", _/binary>>) ->
- rabbit_misc:protocol_error(
- access_refused,
- "~s name '~s' contains reserved prefix 'amq.*'",[Kind, NameBin]);
-check_name(_Kind, NameBin) ->
- NameBin.
-
-maybe_set_fast_reply_to(
- C = #content{properties = P = #'P_basic'{reply_to =
- <<"amq.rabbitmq.reply-to">>}},
- #ch{reply_consumer = ReplyConsumer}) ->
- case ReplyConsumer of
- none -> rabbit_misc:protocol_error(
- precondition_failed,
- "fast reply consumer does not exist", []);
- {_, Suf, _K} -> Rep = <<"amq.rabbitmq.reply-to.", Suf/binary>>,
- rabbit_binary_generator:clear_encoded_content(
- C#content{properties = P#'P_basic'{reply_to = Rep}})
- end;
-maybe_set_fast_reply_to(C, _State) ->
- C.
-
-record_confirms([], State) ->
- State;
-record_confirms(MXs, State = #ch{confirmed = C}) ->
- State#ch{confirmed = [MXs | C]}.
-
-handle_method({Method, Content}, State) ->
- handle_method(Method, Content, State).
-
-handle_method(#'channel.open'{}, _, State = #ch{state = starting}) ->
- %% Don't leave "starting" as the state for 5s. TODO is this TRTTD?
- State1 = State#ch{state = running},
- rabbit_event:if_enabled(State1, #ch.stats_timer,
- fun() -> emit_stats(State1) end),
- {reply, #'channel.open_ok'{}, State1};
-
-handle_method(#'channel.open'{}, _, _State) ->
- rabbit_misc:protocol_error(
- command_invalid, "second 'channel.open' seen", []);
-
-handle_method(_Method, _, #ch{state = starting}) ->
- rabbit_misc:protocol_error(channel_error, "expected 'channel.open'", []);
-
-handle_method(#'channel.close_ok'{}, _, #ch{state = closing}) ->
- stop;
-
-handle_method(#'channel.close'{}, _, State = #ch{writer_pid = WriterPid,
- state = closing}) ->
- ok = rabbit_writer:send_command(WriterPid, #'channel.close_ok'{}),
- {noreply, State};
-
-handle_method(_Method, _, State = #ch{state = closing}) ->
- {noreply, State};
-
-handle_method(#'channel.close'{}, _, State = #ch{reader_pid = ReaderPid}) ->
- {ok, State1} = notify_queues(State),
- %% We issue the channel.close_ok response after a handshake with
- %% the reader, the other half of which is ready_for_close. That
- %% way the reader forgets about the channel before we send the
- %% response (and this channel process terminates). If we didn't do
- %% that, a channel.open for the same channel number, which a
- %% client is entitled to send as soon as it has received the
- %% close_ok, might be received by the reader before it has seen
- %% the termination and hence be sent to the old, now dead/dying
- %% channel process, instead of a new process, and thus lost.
- ReaderPid ! {channel_closing, self()},
- {noreply, State1};
-
-%% Even though the spec prohibits the client from sending commands
-%% while waiting for the reply to a synchronous command, we generally
-%% do allow this...except in the case of a pending tx.commit, where
-%% it could wreak havoc.
-handle_method(_Method, _, #ch{tx = Tx})
- when Tx =:= committing orelse Tx =:= failed ->
- rabbit_misc:protocol_error(
- channel_error, "unexpected command while processing 'tx.commit'", []);
-
-handle_method(#'access.request'{},_, State) ->
- {reply, #'access.request_ok'{ticket = 1}, State};
-
-handle_method(#'basic.publish'{immediate = true}, _Content, _State) ->
- rabbit_misc:protocol_error(not_implemented, "immediate=true", []);
-
-handle_method(#'basic.publish'{exchange = ExchangeNameBin,
- routing_key = RoutingKey,
- mandatory = Mandatory},
- Content, State = #ch{virtual_host = VHostPath,
- tx = Tx,
- channel = ChannelNum,
- confirm_enabled = ConfirmEnabled,
- trace_state = TraceState,
- user = #user{username = Username},
- conn_name = ConnName,
- delivery_flow = Flow}) ->
- check_msg_size(Content),
- ExchangeName = rabbit_misc:r(VHostPath, exchange, ExchangeNameBin),
- check_write_permitted(ExchangeName, State),
- Exchange = rabbit_exchange:lookup_or_die(ExchangeName),
- check_internal_exchange(Exchange),
- %% We decode the content's properties here because we're almost
- %% certain to want to look at delivery-mode and priority.
- DecodedContent = #content {properties = Props} =
- maybe_set_fast_reply_to(
- rabbit_binary_parser:ensure_content_decoded(Content), State),
- check_user_id_header(Props, State),
- check_expiration_header(Props),
- DoConfirm = Tx =/= none orelse ConfirmEnabled,
- {MsgSeqNo, State1} =
- case DoConfirm orelse Mandatory of
- false -> {undefined, State};
- true -> SeqNo = State#ch.publish_seqno,
- {SeqNo, State#ch{publish_seqno = SeqNo + 1}}
- end,
- case rabbit_basic:message(ExchangeName, RoutingKey, DecodedContent) of
- {ok, Message} ->
- Delivery = rabbit_basic:delivery(
- Mandatory, DoConfirm, Message, MsgSeqNo),
- QNames = rabbit_exchange:route(Exchange, Delivery),
- rabbit_trace:tap_in(Message, QNames, ConnName, ChannelNum,
- Username, TraceState),
- DQ = {Delivery#delivery{flow = Flow}, QNames},
- {noreply, case Tx of
- none -> deliver_to_queues(DQ, State1);
- {Msgs, Acks} -> Msgs1 = queue:in(DQ, Msgs),
- State1#ch{tx = {Msgs1, Acks}}
- end};
- {error, Reason} ->
- precondition_failed("invalid message: ~p", [Reason])
- end;
-
-handle_method(#'basic.nack'{delivery_tag = DeliveryTag,
- multiple = Multiple,
- requeue = Requeue}, _, State) ->
- reject(DeliveryTag, Requeue, Multiple, State);
-
-handle_method(#'basic.ack'{delivery_tag = DeliveryTag,
- multiple = Multiple},
- _, State = #ch{unacked_message_q = UAMQ, tx = Tx}) ->
- {Acked, Remaining} = collect_acks(UAMQ, DeliveryTag, Multiple),
- State1 = State#ch{unacked_message_q = Remaining},
- {noreply, case Tx of
- none -> ack(Acked, State1),
- State1;
- {Msgs, Acks} -> Acks1 = ack_cons(ack, Acked, Acks),
- State1#ch{tx = {Msgs, Acks1}}
- end};
-
-handle_method(#'basic.get'{queue = QueueNameBin, no_ack = NoAck},
- _, State = #ch{writer_pid = WriterPid,
- conn_pid = ConnPid,
- limiter = Limiter,
- next_tag = DeliveryTag}) ->
- QueueName = qbin_to_resource(QueueNameBin, State),
- check_read_permitted(QueueName, State),
- case rabbit_amqqueue:with_exclusive_access_or_die(
- QueueName, ConnPid,
- fun (Q) -> rabbit_amqqueue:basic_get(
- Q, self(), NoAck, rabbit_limiter:pid(Limiter))
- end) of
- {ok, MessageCount,
- Msg = {QName, QPid, _MsgId, Redelivered,
- #basic_message{exchange_name = ExchangeName,
- routing_keys = [RoutingKey | _CcRoutes],
- content = Content}}} ->
- ok = rabbit_writer:send_command(
- WriterPid,
- #'basic.get_ok'{delivery_tag = DeliveryTag,
- redelivered = Redelivered,
- exchange = ExchangeName#resource.name,
- routing_key = RoutingKey,
- message_count = MessageCount},
- Content),
- State1 = monitor_delivering_queue(NoAck, QPid, QName, State),
- {noreply, record_sent(none, not(NoAck), Msg, State1)};
- empty ->
- {reply, #'basic.get_empty'{}, State}
- end;
-
-handle_method(#'basic.consume'{queue = <<"amq.rabbitmq.reply-to">>,
- consumer_tag = CTag0,
- no_ack = NoAck,
- nowait = NoWait},
- _, State = #ch{reply_consumer = ReplyConsumer,
- consumer_mapping = ConsumerMapping}) ->
- case dict:find(CTag0, ConsumerMapping) of
- error ->
- case {ReplyConsumer, NoAck} of
- {none, true} ->
- CTag = case CTag0 of
- <<>> -> rabbit_guid:binary(
- rabbit_guid:gen_secure(), "amq.ctag");
- Other -> Other
- end,
- %% Precalculate both suffix and key; base64 encoding is
- %% expensive
- Key = base64:encode(rabbit_guid:gen_secure()),
- PidEnc = base64:encode(term_to_binary(self())),
- Suffix = <<PidEnc/binary, ".", Key/binary>>,
- Consumer = {CTag, Suffix, binary_to_list(Key)},
- State1 = State#ch{reply_consumer = Consumer},
- case NoWait of
- true -> {noreply, State1};
- false -> Rep = #'basic.consume_ok'{consumer_tag = CTag},
- {reply, Rep, State1}
- end;
- {_, false} ->
- rabbit_misc:protocol_error(
- precondition_failed,
- "reply consumer cannot acknowledge", []);
- _ ->
- rabbit_misc:protocol_error(
- precondition_failed, "reply consumer already set", [])
- end;
- {ok, _} ->
- %% Attempted reuse of consumer tag.
- rabbit_misc:protocol_error(
- not_allowed, "attempt to reuse consumer tag '~s'", [CTag0])
- end;
-
-handle_method(#'basic.cancel'{consumer_tag = ConsumerTag, nowait = NoWait},
- _, State = #ch{reply_consumer = {ConsumerTag, _, _}}) ->
- State1 = State#ch{reply_consumer = none},
- case NoWait of
- true -> {noreply, State1};
- false -> Rep = #'basic.cancel_ok'{consumer_tag = ConsumerTag},
- {reply, Rep, State1}
- end;
-
-handle_method(#'basic.consume'{queue = QueueNameBin,
- consumer_tag = ConsumerTag,
- no_local = _, % FIXME: implement
- no_ack = NoAck,
- exclusive = ExclusiveConsume,
- nowait = NoWait,
- arguments = Args},
- _, State = #ch{consumer_prefetch = ConsumerPrefetch,
- consumer_mapping = ConsumerMapping}) ->
- case dict:find(ConsumerTag, ConsumerMapping) of
- error ->
- QueueName = qbin_to_resource(QueueNameBin, State),
- check_read_permitted(QueueName, State),
- ActualConsumerTag =
- case ConsumerTag of
- <<>> -> rabbit_guid:binary(rabbit_guid:gen_secure(),
- "amq.ctag");
- Other -> Other
- end,
- case basic_consume(
- QueueName, NoAck, ConsumerPrefetch, ActualConsumerTag,
- ExclusiveConsume, Args, NoWait, State) of
- {ok, State1} ->
- {noreply, State1};
- {error, exclusive_consume_unavailable} ->
- rabbit_misc:protocol_error(
- access_refused, "~s in exclusive use",
- [rabbit_misc:rs(QueueName)])
- end;
- {ok, _} ->
- %% Attempted reuse of consumer tag.
- rabbit_misc:protocol_error(
- not_allowed, "attempt to reuse consumer tag '~s'", [ConsumerTag])
- end;
-
-handle_method(#'basic.cancel'{consumer_tag = ConsumerTag, nowait = NoWait},
- _, State = #ch{consumer_mapping = ConsumerMapping,
- queue_consumers = QCons}) ->
- OkMsg = #'basic.cancel_ok'{consumer_tag = ConsumerTag},
- case dict:find(ConsumerTag, ConsumerMapping) of
- error ->
- %% Spec requires we ignore this situation.
- return_ok(State, NoWait, OkMsg);
- {ok, {Q = #amqqueue{pid = QPid}, _CParams}} ->
- ConsumerMapping1 = dict:erase(ConsumerTag, ConsumerMapping),
- QCons1 =
- case dict:find(QPid, QCons) of
- error -> QCons;
- {ok, CTags} -> CTags1 = gb_sets:delete(ConsumerTag, CTags),
- case gb_sets:is_empty(CTags1) of
- true -> dict:erase(QPid, QCons);
- false -> dict:store(QPid, CTags1, QCons)
- end
- end,
- NewState = State#ch{consumer_mapping = ConsumerMapping1,
- queue_consumers = QCons1},
- %% In order to ensure that no more messages are sent to
- %% the consumer after the cancel_ok has been sent, we get
- %% the queue process to send the cancel_ok on our
- %% behalf. If we were sending the cancel_ok ourselves it
- %% might overtake a message sent previously by the queue.
- case rabbit_misc:with_exit_handler(
- fun () -> {error, not_found} end,
- fun () ->
- rabbit_amqqueue:basic_cancel(
- Q, self(), ConsumerTag, ok_msg(NoWait, OkMsg))
- end) of
- ok ->
- {noreply, NewState};
- {error, not_found} ->
- %% Spec requires we ignore this situation.
- return_ok(NewState, NoWait, OkMsg)
- end
- end;
-
-handle_method(#'basic.qos'{prefetch_size = Size}, _, _State) when Size /= 0 ->
- rabbit_misc:protocol_error(not_implemented,
- "prefetch_size!=0 (~w)", [Size]);
-
-handle_method(#'basic.qos'{global = false,
- prefetch_count = PrefetchCount}, _, State) ->
- {reply, #'basic.qos_ok'{}, State#ch{consumer_prefetch = PrefetchCount}};
-
-handle_method(#'basic.qos'{global = true,
- prefetch_count = 0},
- _, State = #ch{limiter = Limiter}) ->
- Limiter1 = rabbit_limiter:unlimit_prefetch(Limiter),
- {reply, #'basic.qos_ok'{}, State#ch{limiter = Limiter1}};
-
-handle_method(#'basic.qos'{global = true,
- prefetch_count = PrefetchCount},
- _, State = #ch{limiter = Limiter, unacked_message_q = UAMQ}) ->
- %% TODO queue:len(UAMQ) is not strictly right since that counts
- %% unacked messages from basic.get too. Pretty obscure though.
- Limiter1 = rabbit_limiter:limit_prefetch(Limiter,
- PrefetchCount, queue:len(UAMQ)),
- case ((not rabbit_limiter:is_active(Limiter)) andalso
- rabbit_limiter:is_active(Limiter1)) of
- true -> rabbit_amqqueue:activate_limit_all(
- consumer_queues(State#ch.consumer_mapping), self());
- false -> ok
- end,
- {reply, #'basic.qos_ok'{}, State#ch{limiter = Limiter1}};
-
-handle_method(#'basic.recover_async'{requeue = true},
- _, State = #ch{unacked_message_q = UAMQ, limiter = Limiter}) ->
- OkFun = fun () -> ok end,
- UAMQL = queue:to_list(UAMQ),
- foreach_per_queue(
- fun (QPid, MsgIds) ->
- rabbit_misc:with_exit_handler(
- OkFun,
- fun () -> rabbit_amqqueue:requeue(QPid, MsgIds, self()) end)
- end, lists:reverse(UAMQL)),
- ok = notify_limiter(Limiter, UAMQL),
- %% No answer required - basic.recover is the newer, synchronous
- %% variant of this method
- {noreply, State#ch{unacked_message_q = queue:new()}};
-
-handle_method(#'basic.recover_async'{requeue = false}, _, _State) ->
- rabbit_misc:protocol_error(not_implemented, "requeue=false", []);
-
-handle_method(#'basic.recover'{requeue = Requeue}, Content, State) ->
- {noreply, State1} = handle_method(#'basic.recover_async'{requeue = Requeue},
- Content, State),
- {reply, #'basic.recover_ok'{}, State1};
-
-handle_method(#'basic.reject'{delivery_tag = DeliveryTag, requeue = Requeue},
- _, State) ->
- reject(DeliveryTag, Requeue, false, State);
-
-handle_method(#'exchange.declare'{exchange = ExchangeNameBin,
- type = TypeNameBin,
- passive = false,
- durable = Durable,
- auto_delete = AutoDelete,
- internal = Internal,
- nowait = NoWait,
- arguments = Args},
- _, State = #ch{virtual_host = VHostPath}) ->
- CheckedType = rabbit_exchange:check_type(TypeNameBin),
- ExchangeName = rabbit_misc:r(VHostPath, exchange, ExchangeNameBin),
- check_not_default_exchange(ExchangeName),
- check_configure_permitted(ExchangeName, State),
- X = case rabbit_exchange:lookup(ExchangeName) of
- {ok, FoundX} -> FoundX;
- {error, not_found} ->
- check_name('exchange', ExchangeNameBin),
- AeKey = <<"alternate-exchange">>,
- case rabbit_misc:r_arg(VHostPath, exchange, Args, AeKey) of
- undefined -> ok;
- {error, {invalid_type, Type}} ->
- precondition_failed(
- "invalid type '~s' for arg '~s' in ~s",
- [Type, AeKey, rabbit_misc:rs(ExchangeName)]);
- AName -> check_read_permitted(ExchangeName, State),
- check_write_permitted(AName, State),
- ok
- end,
- rabbit_exchange:declare(ExchangeName,
- CheckedType,
- Durable,
- AutoDelete,
- Internal,
- Args)
- end,
- ok = rabbit_exchange:assert_equivalence(X, CheckedType, Durable,
- AutoDelete, Internal, Args),
- return_ok(State, NoWait, #'exchange.declare_ok'{});
-
-handle_method(#'exchange.declare'{exchange = ExchangeNameBin,
- passive = true,
- nowait = NoWait},
- _, State = #ch{virtual_host = VHostPath}) ->
- ExchangeName = rabbit_misc:r(VHostPath, exchange, ExchangeNameBin),
- check_not_default_exchange(ExchangeName),
- _ = rabbit_exchange:lookup_or_die(ExchangeName),
- return_ok(State, NoWait, #'exchange.declare_ok'{});
-
-handle_method(#'exchange.delete'{exchange = ExchangeNameBin,
- if_unused = IfUnused,
- nowait = NoWait},
- _, State = #ch{virtual_host = VHostPath}) ->
- ExchangeName = rabbit_misc:r(VHostPath, exchange, ExchangeNameBin),
- check_not_default_exchange(ExchangeName),
- check_exchange_deletion(ExchangeName),
- check_configure_permitted(ExchangeName, State),
- case rabbit_exchange:delete(ExchangeName, IfUnused) of
- {error, not_found} ->
- return_ok(State, NoWait, #'exchange.delete_ok'{});
- {error, in_use} ->
- precondition_failed("~s in use", [rabbit_misc:rs(ExchangeName)]);
- ok ->
- return_ok(State, NoWait, #'exchange.delete_ok'{})
- end;
-
-handle_method(#'exchange.bind'{destination = DestinationNameBin,
- source = SourceNameBin,
- routing_key = RoutingKey,
- nowait = NoWait,
- arguments = Arguments}, _, State) ->
- binding_action(fun rabbit_binding:add/2,
- SourceNameBin, exchange, DestinationNameBin, RoutingKey,
- Arguments, #'exchange.bind_ok'{}, NoWait, State);
-
-handle_method(#'exchange.unbind'{destination = DestinationNameBin,
- source = SourceNameBin,
- routing_key = RoutingKey,
- nowait = NoWait,
- arguments = Arguments}, _, State) ->
- binding_action(fun rabbit_binding:remove/2,
- SourceNameBin, exchange, DestinationNameBin, RoutingKey,
- Arguments, #'exchange.unbind_ok'{}, NoWait, State);
-
-%% Note that all declares to these are effectively passive. If it
-%% exists it by definition has one consumer.
-handle_method(#'queue.declare'{queue = <<"amq.rabbitmq.reply-to",
- _/binary>> = QueueNameBin,
- nowait = NoWait}, _,
- State = #ch{virtual_host = VHost}) ->
- QueueName = rabbit_misc:r(VHost, queue, QueueNameBin),
- case declare_fast_reply_to(QueueNameBin) of
- exists -> return_queue_declare_ok(QueueName, NoWait, 0, 1, State);
- not_found -> rabbit_misc:not_found(QueueName)
- end;
-
-handle_method(#'queue.declare'{queue = QueueNameBin,
- passive = false,
- durable = DurableDeclare,
- exclusive = ExclusiveDeclare,
- auto_delete = AutoDelete,
- nowait = NoWait,
- arguments = Args} = Declare,
- _, State = #ch{virtual_host = VHostPath,
- conn_pid = ConnPid,
- queue_collector_pid = CollectorPid}) ->
- Owner = case ExclusiveDeclare of
- true -> ConnPid;
- false -> none
- end,
- Durable = DurableDeclare andalso not ExclusiveDeclare,
- ActualNameBin = case QueueNameBin of
- <<>> -> rabbit_guid:binary(rabbit_guid:gen_secure(),
- "amq.gen");
- Other -> check_name('queue', Other)
- end,
- QueueName = rabbit_misc:r(VHostPath, queue, ActualNameBin),
- check_configure_permitted(QueueName, State),
- case rabbit_amqqueue:with(
- QueueName,
- fun (Q) -> ok = rabbit_amqqueue:assert_equivalence(
- Q, Durable, AutoDelete, Args, Owner),
- maybe_stat(NoWait, Q)
- end) of
- {ok, MessageCount, ConsumerCount} ->
- return_queue_declare_ok(QueueName, NoWait, MessageCount,
- ConsumerCount, State);
- {error, not_found} ->
- DlxKey = <<"x-dead-letter-exchange">>,
- case rabbit_misc:r_arg(VHostPath, exchange, Args, DlxKey) of
- undefined ->
- ok;
- {error, {invalid_type, Type}} ->
- precondition_failed(
- "invalid type '~s' for arg '~s' in ~s",
- [Type, DlxKey, rabbit_misc:rs(QueueName)]);
- DLX ->
- check_read_permitted(QueueName, State),
- check_write_permitted(DLX, State),
- ok
- end,
- case rabbit_amqqueue:declare(QueueName, Durable, AutoDelete,
- Args, Owner) of
- {new, #amqqueue{pid = QPid}} ->
- %% We need to notify the reader within the channel
- %% process so that we can be sure there are no
- %% outstanding exclusive queues being declared as
- %% the connection shuts down.
- ok = case Owner of
- none -> ok;
- _ -> rabbit_queue_collector:register(
- CollectorPid, QPid)
- end,
- return_queue_declare_ok(QueueName, NoWait, 0, 0, State);
- {existing, _Q} ->
- %% must have been created between the stat and the
- %% declare. Loop around again.
- handle_method(Declare, none, State);
- {absent, Q, Reason} ->
- rabbit_misc:absent(Q, Reason);
- {owner_died, _Q} ->
- %% Presumably our own days are numbered since the
- %% connection has died. Pretend the queue exists though,
- %% just so nothing fails.
- return_queue_declare_ok(QueueName, NoWait, 0, 0, State)
- end;
- {error, {absent, Q, Reason}} ->
- rabbit_misc:absent(Q, Reason)
- end;
-
-handle_method(#'queue.declare'{queue = QueueNameBin,
- passive = true,
- nowait = NoWait},
- _, State = #ch{virtual_host = VHostPath,
- conn_pid = ConnPid}) ->
- QueueName = rabbit_misc:r(VHostPath, queue, QueueNameBin),
- {{ok, MessageCount, ConsumerCount}, #amqqueue{} = Q} =
- rabbit_amqqueue:with_or_die(
- QueueName, fun (Q) -> {maybe_stat(NoWait, Q), Q} end),
- ok = rabbit_amqqueue:check_exclusive_access(Q, ConnPid),
- return_queue_declare_ok(QueueName, NoWait, MessageCount, ConsumerCount,
- State);
-
-handle_method(#'queue.delete'{queue = QueueNameBin,
- if_unused = IfUnused,
- if_empty = IfEmpty,
- nowait = NoWait},
- _, State = #ch{conn_pid = ConnPid}) ->
- QueueName = qbin_to_resource(QueueNameBin, State),
- check_configure_permitted(QueueName, State),
- case rabbit_amqqueue:with(
- QueueName,
- fun (Q) ->
- rabbit_amqqueue:check_exclusive_access(Q, ConnPid),
- rabbit_amqqueue:delete(Q, IfUnused, IfEmpty)
- end,
- fun (not_found) -> {ok, 0};
- ({absent, Q, crashed}) -> rabbit_amqqueue:delete_crashed(Q),
- {ok, 0};
- ({absent, Q, Reason}) -> rabbit_misc:absent(Q, Reason)
- end) of
- {error, in_use} ->
- precondition_failed("~s in use", [rabbit_misc:rs(QueueName)]);
- {error, not_empty} ->
- precondition_failed("~s not empty", [rabbit_misc:rs(QueueName)]);
- {ok, PurgedMessageCount} ->
- return_ok(State, NoWait,
- #'queue.delete_ok'{message_count = PurgedMessageCount})
- end;
-
-handle_method(#'queue.bind'{queue = QueueNameBin,
- exchange = ExchangeNameBin,
- routing_key = RoutingKey,
- nowait = NoWait,
- arguments = Arguments}, _, State) ->
- binding_action(fun rabbit_binding:add/2,
- ExchangeNameBin, queue, QueueNameBin, RoutingKey, Arguments,
- #'queue.bind_ok'{}, NoWait, State);
-
-handle_method(#'queue.unbind'{queue = QueueNameBin,
- exchange = ExchangeNameBin,
- routing_key = RoutingKey,
- arguments = Arguments}, _, State) ->
- binding_action(fun rabbit_binding:remove/2,
- ExchangeNameBin, queue, QueueNameBin, RoutingKey, Arguments,
- #'queue.unbind_ok'{}, false, State);
-
-handle_method(#'queue.purge'{queue = QueueNameBin, nowait = NoWait},
- _, State = #ch{conn_pid = ConnPid}) ->
- QueueName = qbin_to_resource(QueueNameBin, State),
- check_read_permitted(QueueName, State),
- {ok, PurgedMessageCount} = rabbit_amqqueue:with_exclusive_access_or_die(
- QueueName, ConnPid,
- fun (Q) -> rabbit_amqqueue:purge(Q) end),
- return_ok(State, NoWait,
- #'queue.purge_ok'{message_count = PurgedMessageCount});
-
-handle_method(#'tx.select'{}, _, #ch{confirm_enabled = true}) ->
- precondition_failed("cannot switch from confirm to tx mode");
-
-handle_method(#'tx.select'{}, _, State = #ch{tx = none}) ->
- {reply, #'tx.select_ok'{}, State#ch{tx = new_tx()}};
-
-handle_method(#'tx.select'{}, _, State) ->
- {reply, #'tx.select_ok'{}, State};
-
-handle_method(#'tx.commit'{}, _, #ch{tx = none}) ->
- precondition_failed("channel is not transactional");
-
-handle_method(#'tx.commit'{}, _, State = #ch{tx = {Msgs, Acks},
- limiter = Limiter}) ->
- State1 = rabbit_misc:queue_fold(fun deliver_to_queues/2, State, Msgs),
- Rev = fun (X) -> lists:reverse(lists:sort(X)) end,
- lists:foreach(fun ({ack, A}) -> ack(Rev(A), State1);
- ({Requeue, A}) -> reject(Requeue, Rev(A), Limiter)
- end, lists:reverse(Acks)),
- {noreply, maybe_complete_tx(State1#ch{tx = committing})};
-
-handle_method(#'tx.rollback'{}, _, #ch{tx = none}) ->
- precondition_failed("channel is not transactional");
-
-handle_method(#'tx.rollback'{}, _, State = #ch{unacked_message_q = UAMQ,
- tx = {_Msgs, Acks}}) ->
- AcksL = lists:append(lists:reverse([lists:reverse(L) || {_, L} <- Acks])),
- UAMQ1 = queue:from_list(lists:usort(AcksL ++ queue:to_list(UAMQ))),
- {reply, #'tx.rollback_ok'{}, State#ch{unacked_message_q = UAMQ1,
- tx = new_tx()}};
-
-handle_method(#'confirm.select'{}, _, #ch{tx = {_, _}}) ->
- precondition_failed("cannot switch from tx to confirm mode");
-
-handle_method(#'confirm.select'{nowait = NoWait}, _, State) ->
- return_ok(State#ch{confirm_enabled = true},
- NoWait, #'confirm.select_ok'{});
-
-handle_method(#'channel.flow'{active = true}, _, State) ->
- {reply, #'channel.flow_ok'{active = true}, State};
-
-handle_method(#'channel.flow'{active = false}, _, _State) ->
- rabbit_misc:protocol_error(not_implemented, "active=false", []);
-
-handle_method(#'basic.credit'{consumer_tag = CTag,
- credit = Credit,
- drain = Drain},
- _, State = #ch{consumer_mapping = Consumers}) ->
- case dict:find(CTag, Consumers) of
- {ok, {Q, _CParams}} -> ok = rabbit_amqqueue:credit(
- Q, self(), CTag, Credit, Drain),
- {noreply, State};
- error -> precondition_failed(
- "unknown consumer tag '~s'", [CTag])
- end;
-
-handle_method(_MethodRecord, _Content, _State) ->
- rabbit_misc:protocol_error(
- command_invalid, "unimplemented method", []).
-
-%%----------------------------------------------------------------------------
-
-%% We get the queue process to send the consume_ok on our behalf. This
-%% is for symmetry with basic.cancel - see the comment in that method
-%% for why.
-basic_consume(QueueName, NoAck, ConsumerPrefetch, ActualConsumerTag,
- ExclusiveConsume, Args, NoWait,
- State = #ch{conn_pid = ConnPid,
- limiter = Limiter,
- consumer_mapping = ConsumerMapping}) ->
- case rabbit_amqqueue:with_exclusive_access_or_die(
- QueueName, ConnPid,
- fun (Q) ->
- {rabbit_amqqueue:basic_consume(
- Q, NoAck, self(),
- rabbit_limiter:pid(Limiter),
- rabbit_limiter:is_active(Limiter),
- ConsumerPrefetch, ActualConsumerTag,
- ExclusiveConsume, Args,
- ok_msg(NoWait, #'basic.consume_ok'{
- consumer_tag = ActualConsumerTag})),
- Q}
- end) of
- {ok, Q = #amqqueue{pid = QPid, name = QName}} ->
- CM1 = dict:store(
- ActualConsumerTag,
- {Q, {NoAck, ConsumerPrefetch, ExclusiveConsume, Args}},
- ConsumerMapping),
- State1 = monitor_delivering_queue(
- NoAck, QPid, QName,
- State#ch{consumer_mapping = CM1}),
- {ok, case NoWait of
- true -> consumer_monitor(ActualConsumerTag, State1);
- false -> State1
- end};
- {{error, exclusive_consume_unavailable} = E, _Q} ->
- E
- end.
-
-maybe_stat(false, Q) -> rabbit_amqqueue:stat(Q);
-maybe_stat(true, _Q) -> {ok, 0, 0}.
-
-consumer_monitor(ConsumerTag,
- State = #ch{consumer_mapping = ConsumerMapping,
- queue_monitors = QMons,
- queue_consumers = QCons}) ->
- {#amqqueue{pid = QPid}, _CParams} =
- dict:fetch(ConsumerTag, ConsumerMapping),
- QCons1 = dict:update(QPid, fun (CTags) ->
- gb_sets:insert(ConsumerTag, CTags)
- end,
- gb_sets:singleton(ConsumerTag), QCons),
- State#ch{queue_monitors = pmon:monitor(QPid, QMons),
- queue_consumers = QCons1}.
-
-monitor_delivering_queue(NoAck, QPid, QName,
- State = #ch{queue_names = QNames,
- queue_monitors = QMons,
- delivering_queues = DQ}) ->
- State#ch{queue_names = dict:store(QPid, QName, QNames),
- queue_monitors = pmon:monitor(QPid, QMons),
- delivering_queues = case NoAck of
- true -> DQ;
- false -> sets:add_element(QPid, DQ)
- end}.
-
-handle_publishing_queue_down(QPid, Reason, State = #ch{unconfirmed = UC,
- mandatory = Mand}) ->
- {MMsgs, Mand1} = dtree:take(QPid, Mand),
- [basic_return(Msg, State, no_route) || {_, Msg} <- MMsgs],
- State1 = State#ch{mandatory = Mand1},
- case rabbit_misc:is_abnormal_exit(Reason) of
- true -> {MXs, UC1} = dtree:take_all(QPid, UC),
- send_nacks(MXs, State1#ch{unconfirmed = UC1});
- false -> {MXs, UC1} = dtree:take(QPid, UC),
- record_confirms(MXs, State1#ch{unconfirmed = UC1})
-
- end.
-
-handle_consuming_queue_down(QPid, State = #ch{queue_consumers = QCons,
- queue_names = QNames}) ->
- ConsumerTags = case dict:find(QPid, QCons) of
- error -> gb_sets:new();
- {ok, CTags} -> CTags
- end,
- gb_sets:fold(
- fun (CTag, StateN = #ch{consumer_mapping = CMap}) ->
- QName = dict:fetch(QPid, QNames),
- case queue_down_consumer_action(CTag, CMap) of
- remove ->
- cancel_consumer(CTag, QName, StateN);
- {recover, {NoAck, ConsumerPrefetch, Exclusive, Args}} ->
- case catch basic_consume( %% [0]
- QName, NoAck, ConsumerPrefetch, CTag,
- Exclusive, Args, true, StateN) of
- {ok, StateN1} -> StateN1;
- _ -> cancel_consumer(CTag, QName, StateN)
- end
- end
- end, State#ch{queue_consumers = dict:erase(QPid, QCons)}, ConsumerTags).
-
-%% [0] There is a slight danger here that if a queue is deleted and
-%% then recreated again the reconsume will succeed even though it was
-%% not an HA failover. But the likelihood is not great and most users
-%% are unlikely to care.
-
-cancel_consumer(CTag, QName, State = #ch{capabilities = Capabilities,
- consumer_mapping = CMap}) ->
- case rabbit_misc:table_lookup(
- Capabilities, <<"consumer_cancel_notify">>) of
- {bool, true} -> ok = send(#'basic.cancel'{consumer_tag = CTag,
- nowait = true}, State);
- _ -> ok
- end,
- rabbit_event:notify(consumer_deleted, [{consumer_tag, CTag},
- {channel, self()},
- {queue, QName}]),
- State#ch{consumer_mapping = dict:erase(CTag, CMap)}.
-
-queue_down_consumer_action(CTag, CMap) ->
- {_, {_, _, _, Args} = ConsumeSpec} = dict:fetch(CTag, CMap),
- case rabbit_misc:table_lookup(Args, <<"x-cancel-on-ha-failover">>) of
- {bool, true} -> remove;
- _ -> {recover, ConsumeSpec}
- end.
-
-handle_delivering_queue_down(QPid, State = #ch{delivering_queues = DQ}) ->
- State#ch{delivering_queues = sets:del_element(QPid, DQ)}.
-
-binding_action(Fun, ExchangeNameBin, DestinationType, DestinationNameBin,
- RoutingKey, Arguments, ReturnMethod, NoWait,
- State = #ch{virtual_host = VHostPath,
- conn_pid = ConnPid }) ->
- DestinationName = name_to_resource(DestinationType, DestinationNameBin, State),
- check_write_permitted(DestinationName, State),
- ExchangeName = rabbit_misc:r(VHostPath, exchange, ExchangeNameBin),
- [check_not_default_exchange(N) || N <- [DestinationName, ExchangeName]],
- check_read_permitted(ExchangeName, State),
- case Fun(#binding{source = ExchangeName,
- destination = DestinationName,
- key = RoutingKey,
- args = Arguments},
- fun (_X, Q = #amqqueue{}) ->
- try rabbit_amqqueue:check_exclusive_access(Q, ConnPid)
- catch exit:Reason -> {error, Reason}
- end;
- (_X, #exchange{}) ->
- ok
- end) of
- {error, {resources_missing, [{not_found, Name} | _]}} ->
- rabbit_misc:not_found(Name);
- {error, {resources_missing, [{absent, Q, Reason} | _]}} ->
- rabbit_misc:absent(Q, Reason);
- {error, binding_not_found} ->
- rabbit_misc:protocol_error(
- not_found, "no binding ~s between ~s and ~s",
- [RoutingKey, rabbit_misc:rs(ExchangeName),
- rabbit_misc:rs(DestinationName)]);
- {error, {binding_invalid, Fmt, Args}} ->
- rabbit_misc:protocol_error(precondition_failed, Fmt, Args);
- {error, #amqp_error{} = Error} ->
- rabbit_misc:protocol_error(Error);
- ok -> return_ok(State, NoWait, ReturnMethod)
- end.
-
-basic_return(#basic_message{exchange_name = ExchangeName,
- routing_keys = [RoutingKey | _CcRoutes],
- content = Content},
- State = #ch{protocol = Protocol, writer_pid = WriterPid},
- Reason) ->
- ?INCR_STATS([{exchange_stats, ExchangeName, 1}], return_unroutable, State),
- {_Close, ReplyCode, ReplyText} = Protocol:lookup_amqp_exception(Reason),
- ok = rabbit_writer:send_command(
- WriterPid,
- #'basic.return'{reply_code = ReplyCode,
- reply_text = ReplyText,
- exchange = ExchangeName#resource.name,
- routing_key = RoutingKey},
- Content).
-
-reject(DeliveryTag, Requeue, Multiple,
- State = #ch{unacked_message_q = UAMQ, tx = Tx}) ->
- {Acked, Remaining} = collect_acks(UAMQ, DeliveryTag, Multiple),
- State1 = State#ch{unacked_message_q = Remaining},
- {noreply, case Tx of
- none -> reject(Requeue, Acked, State1#ch.limiter),
- State1;
- {Msgs, Acks} -> Acks1 = ack_cons(Requeue, Acked, Acks),
- State1#ch{tx = {Msgs, Acks1}}
- end}.
-
-%% NB: Acked is in youngest-first order
-reject(Requeue, Acked, Limiter) ->
- foreach_per_queue(
- fun (QPid, MsgIds) ->
- rabbit_amqqueue:reject(QPid, Requeue, MsgIds, self())
- end, Acked),
- ok = notify_limiter(Limiter, Acked).
-
-record_sent(ConsumerTag, AckRequired,
- Msg = {QName, QPid, MsgId, Redelivered, _Message},
- State = #ch{unacked_message_q = UAMQ,
- next_tag = DeliveryTag,
- trace_state = TraceState,
- user = #user{username = Username},
- conn_name = ConnName,
- channel = ChannelNum}) ->
- ?INCR_STATS([{queue_stats, QName, 1}], case {ConsumerTag, AckRequired} of
- {none, true} -> get;
- {none, false} -> get_no_ack;
- {_ , true} -> deliver;
- {_ , false} -> deliver_no_ack
- end, State),
- case Redelivered of
- true -> ?INCR_STATS([{queue_stats, QName, 1}], redeliver, State);
- false -> ok
- end,
- rabbit_trace:tap_out(Msg, ConnName, ChannelNum, Username, TraceState),
- UAMQ1 = case AckRequired of
- true -> queue:in({DeliveryTag, ConsumerTag, {QPid, MsgId}},
- UAMQ);
- false -> UAMQ
- end,
- State#ch{unacked_message_q = UAMQ1, next_tag = DeliveryTag + 1}.
-
-%% NB: returns acks in youngest-first order
-collect_acks(Q, 0, true) ->
- {lists:reverse(queue:to_list(Q)), queue:new()};
-collect_acks(Q, DeliveryTag, Multiple) ->
- collect_acks([], [], Q, DeliveryTag, Multiple).
-
-collect_acks(ToAcc, PrefixAcc, Q, DeliveryTag, Multiple) ->
- case queue:out(Q) of
- {{value, UnackedMsg = {CurrentDeliveryTag, _ConsumerTag, _Msg}},
- QTail} ->
- if CurrentDeliveryTag == DeliveryTag ->
- {[UnackedMsg | ToAcc],
- case PrefixAcc of
- [] -> QTail;
- _ -> queue:join(
- queue:from_list(lists:reverse(PrefixAcc)),
- QTail)
- end};
- Multiple ->
- collect_acks([UnackedMsg | ToAcc], PrefixAcc,
- QTail, DeliveryTag, Multiple);
- true ->
- collect_acks(ToAcc, [UnackedMsg | PrefixAcc],
- QTail, DeliveryTag, Multiple)
- end;
- {empty, _} ->
- precondition_failed("unknown delivery tag ~w", [DeliveryTag])
- end.
-
-%% NB: Acked is in youngest-first order
-ack(Acked, State = #ch{queue_names = QNames}) ->
- foreach_per_queue(
- fun (QPid, MsgIds) ->
- ok = rabbit_amqqueue:ack(QPid, MsgIds, self()),
- ?INCR_STATS(case dict:find(QPid, QNames) of
- {ok, QName} -> Count = length(MsgIds),
- [{queue_stats, QName, Count}];
- error -> []
- end, ack, State)
- end, Acked),
- ok = notify_limiter(State#ch.limiter, Acked).
-
-%% {Msgs, Acks}
-%%
-%% Msgs is a queue.
-%%
-%% Acks looks s.t. like this:
-%% [{false,[5,4]},{true,[3]},{ack,[2,1]}, ...]
-%%
-%% Each element is a pair consisting of a tag and a list of
-%% ack'ed/reject'ed msg ids. The tag is one of 'ack' (to ack), 'true'
-%% (reject w requeue), 'false' (reject w/o requeue). The msg ids, as
-%% well as the list overall, are in "most-recent (generally youngest)
-%% ack first" order.
-new_tx() -> {queue:new(), []}.
-
-notify_queues(State = #ch{state = closing}) ->
- {ok, State};
-notify_queues(State = #ch{consumer_mapping = Consumers,
- delivering_queues = DQ }) ->
- QPids = sets:to_list(
- sets:union(sets:from_list(consumer_queues(Consumers)), DQ)),
- {rabbit_amqqueue:notify_down_all(QPids, self()), State#ch{state = closing}}.
-
-foreach_per_queue(_F, []) ->
- ok;
-foreach_per_queue(F, [{_DTag, _CTag, {QPid, MsgId}}]) -> %% common case
- F(QPid, [MsgId]);
-%% NB: UAL should be in youngest-first order; the tree values will
-%% then be in oldest-first order
-foreach_per_queue(F, UAL) ->
- T = lists:foldl(fun ({_DTag, _CTag, {QPid, MsgId}}, T) ->
- rabbit_misc:gb_trees_cons(QPid, MsgId, T)
- end, gb_trees:empty(), UAL),
- rabbit_misc:gb_trees_foreach(F, T).
-
-consumer_queues(Consumers) ->
- lists:usort([QPid || {_Key, {#amqqueue{pid = QPid}, _CParams}}
- <- dict:to_list(Consumers)]).
-
-%% tell the limiter about the number of acks that have been received
-%% for messages delivered to subscribed consumers, but not acks for
-%% messages sent in a response to a basic.get (identified by their
-%% 'none' consumer tag)
-notify_limiter(Limiter, Acked) ->
- %% optimisation: avoid the potentially expensive 'foldl' in the
- %% common case.
- case rabbit_limiter:is_active(Limiter) of
- false -> ok;
- true -> case lists:foldl(fun ({_, none, _}, Acc) -> Acc;
- ({_, _, _}, Acc) -> Acc + 1
- end, 0, Acked) of
- 0 -> ok;
- Count -> rabbit_limiter:ack(Limiter, Count)
- end
- end.
-
-deliver_to_queues({#delivery{message = #basic_message{exchange_name = XName},
- confirm = false,
- mandatory = false},
- []}, State) -> %% optimisation
- ?INCR_STATS([{exchange_stats, XName, 1}], publish, State),
- State;
-deliver_to_queues({Delivery = #delivery{message = Message = #basic_message{
- exchange_name = XName},
- mandatory = Mandatory,
- confirm = Confirm,
- msg_seq_no = MsgSeqNo},
- DelQNames}, State = #ch{queue_names = QNames,
- queue_monitors = QMons}) ->
- Qs = rabbit_amqqueue:lookup(DelQNames),
- DeliveredQPids = rabbit_amqqueue:deliver(Qs, Delivery),
- %% The pmon:monitor_all/2 monitors all queues to which we
- %% delivered. But we want to monitor even queues we didn't deliver
- %% to, since we need their 'DOWN' messages to clean
- %% queue_names. So we also need to monitor each QPid from
- %% queues. But that only gets the masters (which is fine for
- %% cleaning queue_names), so we need the union of both.
- %%
- %% ...and we need to add even non-delivered queues to queue_names
- %% since alternative algorithms to update queue_names less
- %% frequently would in fact be more expensive in the common case.
- {QNames1, QMons1} =
- lists:foldl(fun (#amqqueue{pid = QPid, name = QName},
- {QNames0, QMons0}) ->
- {case dict:is_key(QPid, QNames0) of
- true -> QNames0;
- false -> dict:store(QPid, QName, QNames0)
- end, pmon:monitor(QPid, QMons0)}
- end, {QNames, pmon:monitor_all(DeliveredQPids, QMons)}, Qs),
- State1 = State#ch{queue_names = QNames1,
- queue_monitors = QMons1},
- %% NB: the order here is important since basic.returns must be
- %% sent before confirms.
- State2 = process_routing_mandatory(Mandatory, DeliveredQPids, MsgSeqNo,
- Message, State1),
- State3 = process_routing_confirm( Confirm, DeliveredQPids, MsgSeqNo,
- XName, State2),
- ?INCR_STATS([{exchange_stats, XName, 1} |
- [{queue_exchange_stats, {QName, XName}, 1} ||
- QPid <- DeliveredQPids,
- {ok, QName} <- [dict:find(QPid, QNames1)]]],
- publish, State3),
- State3.
-
-process_routing_mandatory(false, _, _MsgSeqNo, _Msg, State) ->
- State;
-process_routing_mandatory(true, [], _MsgSeqNo, Msg, State) ->
- ok = basic_return(Msg, State, no_route),
- State;
-process_routing_mandatory(true, QPids, MsgSeqNo, Msg, State) ->
- State#ch{mandatory = dtree:insert(MsgSeqNo, QPids, Msg,
- State#ch.mandatory)}.
-
-process_routing_confirm(false, _, _MsgSeqNo, _XName, State) ->
- State;
-process_routing_confirm(true, [], MsgSeqNo, XName, State) ->
- record_confirms([{MsgSeqNo, XName}], State);
-process_routing_confirm(true, QPids, MsgSeqNo, XName, State) ->
- State#ch{unconfirmed = dtree:insert(MsgSeqNo, QPids, XName,
- State#ch.unconfirmed)}.
-
-send_nacks([], State) ->
- State;
-send_nacks(_MXs, State = #ch{state = closing,
- tx = none}) -> %% optimisation
- State;
-send_nacks(MXs, State = #ch{tx = none}) ->
- coalesce_and_send([MsgSeqNo || {MsgSeqNo, _} <- MXs],
- fun(MsgSeqNo, Multiple) ->
- #'basic.nack'{delivery_tag = MsgSeqNo,
- multiple = Multiple}
- end, State);
-send_nacks(_MXs, State = #ch{state = closing}) -> %% optimisation
- State#ch{tx = failed};
-send_nacks(_, State) ->
- maybe_complete_tx(State#ch{tx = failed}).
-
-send_confirms(State = #ch{tx = none, confirmed = []}) ->
- State;
-send_confirms(State = #ch{tx = none, confirmed = C}) ->
- case rabbit_node_monitor:pause_partition_guard() of
- ok -> MsgSeqNos =
- lists:foldl(
- fun ({MsgSeqNo, XName}, MSNs) ->
- ?INCR_STATS([{exchange_stats, XName, 1}],
- confirm, State),
- [MsgSeqNo | MSNs]
- end, [], lists:append(C)),
- send_confirms(MsgSeqNos, State#ch{confirmed = []});
- pausing -> State
- end;
-send_confirms(State) ->
- case rabbit_node_monitor:pause_partition_guard() of
- ok -> maybe_complete_tx(State);
- pausing -> State
- end.
-
-send_confirms([], State) ->
- State;
-send_confirms(_Cs, State = #ch{state = closing}) -> %% optimisation
- State;
-send_confirms([MsgSeqNo], State) ->
- ok = send(#'basic.ack'{delivery_tag = MsgSeqNo}, State),
- State;
-send_confirms(Cs, State) ->
- coalesce_and_send(Cs, fun(MsgSeqNo, Multiple) ->
- #'basic.ack'{delivery_tag = MsgSeqNo,
- multiple = Multiple}
- end, State).
-
-coalesce_and_send(MsgSeqNos, MkMsgFun, State = #ch{unconfirmed = UC}) ->
- SMsgSeqNos = lists:usort(MsgSeqNos),
- CutOff = case dtree:is_empty(UC) of
- true -> lists:last(SMsgSeqNos) + 1;
- false -> {SeqNo, _XName} = dtree:smallest(UC), SeqNo
- end,
- {Ms, Ss} = lists:splitwith(fun(X) -> X < CutOff end, SMsgSeqNos),
- case Ms of
- [] -> ok;
- _ -> ok = send(MkMsgFun(lists:last(Ms), true), State)
- end,
- [ok = send(MkMsgFun(SeqNo, false), State) || SeqNo <- Ss],
- State.
-
-ack_cons(Tag, Acked, [{Tag, Acks} | L]) -> [{Tag, Acked ++ Acks} | L];
-ack_cons(Tag, Acked, Acks) -> [{Tag, Acked} | Acks].
-
-ack_len(Acks) -> lists:sum([length(L) || {ack, L} <- Acks]).
-
-maybe_complete_tx(State = #ch{tx = {_, _}}) ->
- State;
-maybe_complete_tx(State = #ch{unconfirmed = UC}) ->
- case dtree:is_empty(UC) of
- false -> State;
- true -> complete_tx(State#ch{confirmed = []})
- end.
-
-complete_tx(State = #ch{tx = committing}) ->
- ok = send(#'tx.commit_ok'{}, State),
- State#ch{tx = new_tx()};
-complete_tx(State = #ch{tx = failed}) ->
- {noreply, State1} = handle_exception(
- rabbit_misc:amqp_error(
- precondition_failed, "partial tx completion", [],
- 'tx.commit'),
- State),
- State1#ch{tx = new_tx()}.
-
-infos(Items, State) -> [{Item, i(Item, State)} || Item <- Items].
-
-i(pid, _) -> self();
-i(connection, #ch{conn_pid = ConnPid}) -> ConnPid;
-i(number, #ch{channel = Channel}) -> Channel;
-i(user, #ch{user = User}) -> User#user.username;
-i(vhost, #ch{virtual_host = VHost}) -> VHost;
-i(transactional, #ch{tx = Tx}) -> Tx =/= none;
-i(confirm, #ch{confirm_enabled = CE}) -> CE;
-i(name, State) -> name(State);
-i(consumer_count, #ch{consumer_mapping = CM}) -> dict:size(CM);
-i(messages_unconfirmed, #ch{unconfirmed = UC}) -> dtree:size(UC);
-i(messages_unacknowledged, #ch{unacked_message_q = UAMQ}) -> queue:len(UAMQ);
-i(messages_uncommitted, #ch{tx = {Msgs, _Acks}}) -> queue:len(Msgs);
-i(messages_uncommitted, #ch{}) -> 0;
-i(acks_uncommitted, #ch{tx = {_Msgs, Acks}}) -> ack_len(Acks);
-i(acks_uncommitted, #ch{}) -> 0;
-i(state, #ch{state = running}) -> credit_flow:state();
-i(state, #ch{state = State}) -> State;
-i(prefetch_count, #ch{consumer_prefetch = C}) -> C;
-i(global_prefetch_count, #ch{limiter = Limiter}) ->
- rabbit_limiter:get_prefetch_limit(Limiter);
-i(Item, _) ->
- throw({bad_argument, Item}).
-
-name(#ch{conn_name = ConnName, channel = Channel}) ->
- list_to_binary(rabbit_misc:format("~s (~p)", [ConnName, Channel])).
-
-incr_stats(Incs, Measure) ->
- [update_measures(Type, Key, Inc, Measure) || {Type, Key, Inc} <- Incs].
-
-update_measures(Type, Key, Inc, Measure) ->
- Measures = case get({Type, Key}) of
- undefined -> [];
- D -> D
- end,
- Cur = case orddict:find(Measure, Measures) of
- error -> 0;
- {ok, C} -> C
- end,
- put({Type, Key}, orddict:store(Measure, Cur + Inc, Measures)).
-
-emit_stats(State) -> emit_stats(State, []).
-
-emit_stats(State, Extra) ->
- Coarse = infos(?STATISTICS_KEYS, State),
- case rabbit_event:stats_level(State, #ch.stats_timer) of
- coarse -> rabbit_event:notify(channel_stats, Extra ++ Coarse);
- fine -> Fine = [{channel_queue_stats,
- [{QName, Stats} ||
- {{queue_stats, QName}, Stats} <- get()]},
- {channel_exchange_stats,
- [{XName, Stats} ||
- {{exchange_stats, XName}, Stats} <- get()]},
- {channel_queue_exchange_stats,
- [{QX, Stats} ||
- {{queue_exchange_stats, QX}, Stats} <- get()]}],
- rabbit_event:notify(channel_stats, Extra ++ Coarse ++ Fine)
- end.
-
-erase_queue_stats(QName) ->
- erase({queue_stats, QName}),
- [erase({queue_exchange_stats, QX}) ||
- {{queue_exchange_stats, QX = {QName0, _}}, _} <- get(),
- QName0 =:= QName].
-
-get_vhost(#ch{virtual_host = VHost}) -> VHost.
-
-get_user(#ch{user = User}) -> User.
diff --git a/src/rabbit_channel_interceptor.erl b/src/rabbit_channel_interceptor.erl
deleted file mode 100644
index 6a925b69fe..0000000000
--- a/src/rabbit_channel_interceptor.erl
+++ /dev/null
@@ -1,117 +0,0 @@
-%% The contents of this file are subject to the Mozilla Public License
-%% Version 1.1 (the "License"); you may not use this file except in
-%% compliance with the License. You may obtain a copy of the License
-%% at http://www.mozilla.org/MPL/
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and
-%% limitations under the License.
-%%
-%% The Original Code is RabbitMQ.
-%%
-%% The Initial Developer of the Original Code is GoPivotal, Inc.
-%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved.
-%%
-
--module(rabbit_channel_interceptor).
-
--include("rabbit_framing.hrl").
--include("rabbit.hrl").
-
--export([init/1, intercept_in/3]).
-
--ifdef(use_specs).
-
--type(method_name() :: rabbit_framing:amqp_method_name()).
--type(original_method() :: rabbit_framing:amqp_method_record()).
--type(processed_method() :: rabbit_framing:amqp_method_record()).
--type(original_content() :: rabbit_types:maybe(rabbit_types:content())).
--type(processed_content() :: rabbit_types:maybe(rabbit_types:content())).
--type(interceptor_state() :: term()).
-
--callback description() -> [proplists:property()].
-%% Derive some initial state from the channel. This will be passed back
-%% as the third argument of intercept/3.
--callback init(rabbit_channel:channel()) -> interceptor_state().
--callback intercept(original_method(), original_content(),
- interceptor_state()) ->
- {processed_method(), processed_content()} |
- rabbit_misc:channel_or_connection_exit().
--callback applies_to() -> list(method_name()).
-
--else.
-
--export([behaviour_info/1]).
-
-behaviour_info(callbacks) ->
- [{description, 0}, {init, 1}, {intercept, 3}, {applies_to, 0}];
-behaviour_info(_Other) ->
- undefined.
-
--endif.
-
-init(Ch) ->
- Mods = [M || {_, M} <- rabbit_registry:lookup_all(channel_interceptor)],
- check_no_overlap(Mods),
- [{Mod, Mod:init(Ch)} || Mod <- Mods].
-
-check_no_overlap(Mods) ->
- check_no_overlap1([sets:from_list(Mod:applies_to()) || Mod <- Mods]).
-
-%% Check no non-empty pairwise intersection in a list of sets
-check_no_overlap1(Sets) ->
- lists:foldl(fun(Set, Union) ->
- Is = sets:intersection(Set, Union),
- case sets:size(Is) of
- 0 -> ok;
- _ ->
- internal_error("Interceptor: more than one "
- "module handles ~p~n", [Is])
- end,
- sets:union(Set, Union)
- end,
- sets:new(),
- Sets),
- ok.
-
-intercept_in(M, C, Mods) ->
- lists:foldl(fun({Mod, ModState}, {M1, C1}) ->
- call_module(Mod, ModState, M1, C1)
- end,
- {M, C},
- Mods).
-
-call_module(Mod, St, M, C) ->
- % this little dance is because Mod might be unloaded at any point
- case (catch {ok, Mod:intercept(M, C, St)}) of
- {ok, R} -> validate_response(Mod, M, C, R);
- {'EXIT', {undef, [{Mod, intercept, _, _} | _]}} -> {M, C}
- end.
-
-validate_response(Mod, M1, C1, R = {M2, C2}) ->
- case {validate_method(M1, M2), validate_content(C1, C2)} of
- {true, true} -> R;
- {false, _} ->
- internal_error("Interceptor: ~p expected to return "
- "method: ~p but returned: ~p",
- [Mod, rabbit_misc:method_record_type(M1),
- rabbit_misc:method_record_type(M2)]);
- {_, false} ->
- internal_error("Interceptor: ~p expected to return "
- "content iff content is provided but "
- "content in = ~p; content out = ~p",
- [Mod, C1, C2])
- end.
-
-validate_method(M, M2) ->
- rabbit_misc:method_record_type(M) =:= rabbit_misc:method_record_type(M2).
-
-validate_content(none, none) -> true;
-validate_content(#content{}, #content{}) -> true;
-validate_content(_, _) -> false.
-
-%% keep dialyzer happy
--spec internal_error(string(), [any()]) -> no_return().
-internal_error(Format, Args) ->
- rabbit_misc:protocol_error(internal_error, Format, Args).
diff --git a/src/rabbit_command_assembler.erl b/src/rabbit_command_assembler.erl
deleted file mode 100644
index f93b85b122..0000000000
--- a/src/rabbit_command_assembler.erl
+++ /dev/null
@@ -1,137 +0,0 @@
-%% The contents of this file are subject to the Mozilla Public License
-%% Version 1.1 (the "License"); you may not use this file except in
-%% compliance with the License. You may obtain a copy of the License
-%% at http://www.mozilla.org/MPL/
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and
-%% limitations under the License.
-%%
-%% The Original Code is RabbitMQ.
-%%
-%% The Initial Developer of the Original Code is GoPivotal, Inc.
-%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved.
-%%
-
--module(rabbit_command_assembler).
--include("rabbit_framing.hrl").
--include("rabbit.hrl").
-
--export([analyze_frame/3, init/1, process/2]).
-
-%%----------------------------------------------------------------------------
-
-%%----------------------------------------------------------------------------
-
--ifdef(use_specs).
-
--export_type([frame/0]).
-
--type(frame_type() :: ?FRAME_METHOD | ?FRAME_HEADER | ?FRAME_BODY |
- ?FRAME_OOB_METHOD | ?FRAME_OOB_HEADER | ?FRAME_OOB_BODY |
- ?FRAME_TRACE | ?FRAME_HEARTBEAT).
--type(protocol() :: rabbit_framing:protocol()).
--type(method() :: rabbit_framing:amqp_method_record()).
--type(class_id() :: rabbit_framing:amqp_class_id()).
--type(weight() :: non_neg_integer()).
--type(body_size() :: non_neg_integer()).
--type(content() :: rabbit_types:undecoded_content()).
-
--type(frame() ::
- {'method', rabbit_framing:amqp_method_name(), binary()} |
- {'content_header', class_id(), weight(), body_size(), binary()} |
- {'content_body', binary()}).
-
--type(state() ::
- {'method', protocol()} |
- {'content_header', method(), class_id(), protocol()} |
- {'content_body', method(), body_size(), class_id(), protocol()}).
-
--spec(analyze_frame/3 :: (frame_type(), binary(), protocol()) ->
- frame() | 'heartbeat' | 'error').
-
--spec(init/1 :: (protocol()) -> {ok, state()}).
--spec(process/2 :: (frame(), state()) ->
- {ok, state()} |
- {ok, method(), state()} |
- {ok, method(), content(), state()} |
- {error, rabbit_types:amqp_error()}).
-
--endif.
-
-%%--------------------------------------------------------------------
-
-analyze_frame(?FRAME_METHOD,
- <<ClassId:16, MethodId:16, MethodFields/binary>>,
- Protocol) ->
- MethodName = Protocol:lookup_method_name({ClassId, MethodId}),
- {method, MethodName, MethodFields};
-analyze_frame(?FRAME_HEADER,
- <<ClassId:16, Weight:16, BodySize:64, Properties/binary>>,
- _Protocol) ->
- {content_header, ClassId, Weight, BodySize, Properties};
-analyze_frame(?FRAME_BODY, Body, _Protocol) ->
- {content_body, Body};
-analyze_frame(?FRAME_HEARTBEAT, <<>>, _Protocol) ->
- heartbeat;
-analyze_frame(_Type, _Body, _Protocol) ->
- error.
-
-init(Protocol) -> {ok, {method, Protocol}}.
-
-process({method, MethodName, FieldsBin}, {method, Protocol}) ->
- try
- Method = Protocol:decode_method_fields(MethodName, FieldsBin),
- case Protocol:method_has_content(MethodName) of
- true -> {ClassId, _MethodId} = Protocol:method_id(MethodName),
- {ok, {content_header, Method, ClassId, Protocol}};
- false -> {ok, Method, {method, Protocol}}
- end
- catch exit:#amqp_error{} = Reason -> {error, Reason}
- end;
-process(_Frame, {method, _Protocol}) ->
- unexpected_frame("expected method frame, "
- "got non method frame instead", [], none);
-process({content_header, ClassId, 0, 0, PropertiesBin},
- {content_header, Method, ClassId, Protocol}) ->
- Content = empty_content(ClassId, PropertiesBin, Protocol),
- {ok, Method, Content, {method, Protocol}};
-process({content_header, ClassId, 0, BodySize, PropertiesBin},
- {content_header, Method, ClassId, Protocol}) ->
- Content = empty_content(ClassId, PropertiesBin, Protocol),
- {ok, {content_body, Method, BodySize, Content, Protocol}};
-process({content_header, HeaderClassId, 0, _BodySize, _PropertiesBin},
- {content_header, Method, ClassId, _Protocol}) ->
- unexpected_frame("expected content header for class ~w, "
- "got one for class ~w instead",
- [ClassId, HeaderClassId], Method);
-process(_Frame, {content_header, Method, ClassId, _Protocol}) ->
- unexpected_frame("expected content header for class ~w, "
- "got non content header frame instead", [ClassId], Method);
-process({content_body, FragmentBin},
- {content_body, Method, RemainingSize,
- Content = #content{payload_fragments_rev = Fragments}, Protocol}) ->
- NewContent = Content#content{
- payload_fragments_rev = [FragmentBin | Fragments]},
- case RemainingSize - size(FragmentBin) of
- 0 -> {ok, Method, NewContent, {method, Protocol}};
- Sz -> {ok, {content_body, Method, Sz, NewContent, Protocol}}
- end;
-process(_Frame, {content_body, Method, _RemainingSize, _Content, _Protocol}) ->
- unexpected_frame("expected content body, "
- "got non content body frame instead", [], Method).
-
-%%--------------------------------------------------------------------
-
-empty_content(ClassId, PropertiesBin, Protocol) ->
- #content{class_id = ClassId,
- properties = none,
- properties_bin = PropertiesBin,
- protocol = Protocol,
- payload_fragments_rev = []}.
-
-unexpected_frame(Format, Params, Method) when is_atom(Method) ->
- {error, rabbit_misc:amqp_error(unexpected_frame, Format, Params, Method)};
-unexpected_frame(Format, Params, Method) ->
- unexpected_frame(Format, Params, rabbit_misc:method_record_type(Method)).
diff --git a/src/rabbit_event.erl b/src/rabbit_event.erl
deleted file mode 100644
index 4d62dd079b..0000000000
--- a/src/rabbit_event.erl
+++ /dev/null
@@ -1,163 +0,0 @@
-%% The contents of this file are subject to the Mozilla Public License
-%% Version 1.1 (the "License"); you may not use this file except in
-%% compliance with the License. You may obtain a copy of the License
-%% at http://www.mozilla.org/MPL/
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and
-%% limitations under the License.
-%%
-%% The Original Code is RabbitMQ.
-%%
-%% The Initial Developer of the Original Code is GoPivotal, Inc.
-%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved.
-%%
-
--module(rabbit_event).
-
--include("rabbit.hrl").
-
--export([start_link/0]).
--export([init_stats_timer/2, init_disabled_stats_timer/2,
- ensure_stats_timer/3, stop_stats_timer/2, reset_stats_timer/2]).
--export([stats_level/2, if_enabled/3]).
--export([notify/2, notify/3, notify_if/3]).
--export([sync_notify/2, sync_notify/3]).
-
-%%----------------------------------------------------------------------------
-
--record(state, {level, interval, timer}).
-
-%%----------------------------------------------------------------------------
-
--ifdef(use_specs).
-
--export_type([event_type/0, event_props/0, event_timestamp/0, event/0]).
-
--type(event_type() :: atom()).
--type(event_props() :: term()).
--type(event_timestamp() :: non_neg_integer()).
-
--type(event() :: #event { type :: event_type(),
- props :: event_props(),
- reference :: 'none' | reference(),
- timestamp :: event_timestamp() }).
-
--type(level() :: 'none' | 'coarse' | 'fine').
-
--type(timer_fun() :: fun (() -> 'ok')).
--type(container() :: tuple()).
--type(pos() :: non_neg_integer()).
-
--spec(start_link/0 :: () -> rabbit_types:ok_pid_or_error()).
--spec(init_stats_timer/2 :: (container(), pos()) -> container()).
--spec(init_disabled_stats_timer/2 :: (container(), pos()) -> container()).
--spec(ensure_stats_timer/3 :: (container(), pos(), term()) -> container()).
--spec(stop_stats_timer/2 :: (container(), pos()) -> container()).
--spec(reset_stats_timer/2 :: (container(), pos()) -> container()).
--spec(stats_level/2 :: (container(), pos()) -> level()).
--spec(if_enabled/3 :: (container(), pos(), timer_fun()) -> 'ok').
--spec(notify/2 :: (event_type(), event_props()) -> 'ok').
--spec(notify/3 :: (event_type(), event_props(), reference() | 'none') -> 'ok').
--spec(notify_if/3 :: (boolean(), event_type(), event_props()) -> 'ok').
--spec(sync_notify/2 :: (event_type(), event_props()) -> 'ok').
--spec(sync_notify/3 :: (event_type(), event_props(),
- reference() | 'none') -> 'ok').
-
--endif.
-
-%%----------------------------------------------------------------------------
-
-start_link() ->
- gen_event:start_link({local, ?MODULE}).
-
-%% The idea is, for each stat-emitting object:
-%%
-%% On startup:
-%% init_stats_timer(State)
-%% notify(created event)
-%% if_enabled(internal_emit_stats) - so we immediately send something
-%%
-%% On wakeup:
-%% ensure_stats_timer(State, emit_stats)
-%% (Note we can't emit stats immediately, the timer may have fired 1ms ago.)
-%%
-%% emit_stats:
-%% if_enabled(internal_emit_stats)
-%% reset_stats_timer(State) - just bookkeeping
-%%
-%% Pre-hibernation:
-%% if_enabled(internal_emit_stats)
-%% stop_stats_timer(State)
-%%
-%% internal_emit_stats:
-%% notify(stats)
-
-init_stats_timer(C, P) ->
- {ok, StatsLevel} = application:get_env(rabbit, collect_statistics),
- {ok, Interval} = application:get_env(rabbit, collect_statistics_interval),
- setelement(P, C, #state{level = StatsLevel, interval = Interval,
- timer = undefined}).
-
-init_disabled_stats_timer(C, P) ->
- setelement(P, C, #state{level = none, interval = 0, timer = undefined}).
-
-ensure_stats_timer(C, P, Msg) ->
- case element(P, C) of
- #state{level = Level, interval = Interval, timer = undefined} = State
- when Level =/= none ->
- TRef = erlang:send_after(Interval, self(), Msg),
- setelement(P, C, State#state{timer = TRef});
- #state{} ->
- C
- end.
-
-stop_stats_timer(C, P) ->
- case element(P, C) of
- #state{timer = TRef} = State when TRef =/= undefined ->
- case erlang:cancel_timer(TRef) of
- false -> C;
- _ -> setelement(P, C, State#state{timer = undefined})
- end;
- #state{} ->
- C
- end.
-
-reset_stats_timer(C, P) ->
- case element(P, C) of
- #state{timer = TRef} = State when TRef =/= undefined ->
- setelement(P, C, State#state{timer = undefined});
- #state{} ->
- C
- end.
-
-stats_level(C, P) ->
- #state{level = Level} = element(P, C),
- Level.
-
-if_enabled(C, P, Fun) ->
- case element(P, C) of
- #state{level = none} -> ok;
- #state{} -> Fun(), ok
- end.
-
-notify_if(true, Type, Props) -> notify(Type, Props);
-notify_if(false, _Type, _Props) -> ok.
-
-notify(Type, Props) -> notify(Type, Props, none).
-
-notify(Type, Props, Ref) ->
- gen_event:notify(?MODULE, event_cons(Type, Props, Ref)).
-
-sync_notify(Type, Props) -> sync_notify(Type, Props, none).
-
-sync_notify(Type, Props, Ref) ->
- gen_event:sync_notify(?MODULE, event_cons(Type, Props, Ref)).
-
-event_cons(Type, Props, Ref) ->
- #event{type = Type,
- props = Props,
- reference = Ref,
- timestamp = time_compat:os_system_time(milli_seconds)}.
-
diff --git a/src/rabbit_exchange_decorator.erl b/src/rabbit_exchange_decorator.erl
deleted file mode 100644
index 7c5bfdf913..0000000000
--- a/src/rabbit_exchange_decorator.erl
+++ /dev/null
@@ -1,128 +0,0 @@
-%% The contents of this file are subject to the Mozilla Public License
-%% Version 1.1 (the "License"); you may not use this file except in
-%% compliance with the License. You may obtain a copy of the License
-%% at http://www.mozilla.org/MPL/
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and
-%% limitations under the License.
-%%
-%% The Original Code is RabbitMQ.
-%%
-%% The Initial Developer of the Original Code is GoPivotal, Inc.
-%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved.
-%%
-
--module(rabbit_exchange_decorator).
-
--include("rabbit.hrl").
-
--export([select/2, set/1, register/2, unregister/1]).
-
-%% This is like an exchange type except that:
-%%
-%% 1) It applies to all exchanges as soon as it is installed, therefore
-%% 2) It is not allowed to affect validation, so no validate/1 or
-%% assert_args_equivalence/2
-%%
-%% It's possible in the future we might make decorators
-%% able to manipulate messages as they are published.
-
--ifdef(use_specs).
-
--type(tx() :: 'transaction' | 'none').
--type(serial() :: pos_integer() | tx()).
-
--callback description() -> [proplists:property()].
-
-%% Should Rabbit ensure that all binding events that are
-%% delivered to an individual exchange can be serialised? (they
-%% might still be delivered out of order, but there'll be a
-%% serial number).
--callback serialise_events(rabbit_types:exchange()) -> boolean().
-
-%% called after declaration and recovery
--callback create(tx(), rabbit_types:exchange()) -> 'ok'.
-
-%% called after exchange (auto)deletion.
--callback delete(tx(), rabbit_types:exchange(), [rabbit_types:binding()]) ->
- 'ok'.
-
-%% called when the policy attached to this exchange changes.
--callback policy_changed(rabbit_types:exchange(), rabbit_types:exchange()) ->
- 'ok'.
-
-%% called after a binding has been added or recovered
--callback add_binding(serial(), rabbit_types:exchange(),
- rabbit_types:binding()) -> 'ok'.
-
-%% called after bindings have been deleted.
--callback remove_bindings(serial(), rabbit_types:exchange(),
- [rabbit_types:binding()]) -> 'ok'.
-
-%% Allows additional destinations to be added to the routing decision.
--callback route(rabbit_types:exchange(), rabbit_types:delivery()) ->
- [rabbit_amqqueue:name() | rabbit_exchange:name()].
-
-%% Whether the decorator wishes to receive callbacks for the exchange
-%% none:no callbacks, noroute:all callbacks except route, all:all callbacks
--callback active_for(rabbit_types:exchange()) -> 'none' | 'noroute' | 'all'.
-
--else.
-
--export([behaviour_info/1]).
-
-behaviour_info(callbacks) ->
- [{description, 0}, {serialise_events, 1}, {create, 2}, {delete, 3},
- {policy_changed, 2}, {add_binding, 3}, {remove_bindings, 3},
- {route, 2}, {active_for, 1}];
-behaviour_info(_Other) ->
- undefined.
-
--endif.
-
-%%----------------------------------------------------------------------------
-
-%% select a subset of active decorators
-select(all, {Route, NoRoute}) -> filter(Route ++ NoRoute);
-select(route, {Route, _NoRoute}) -> filter(Route);
-select(raw, {Route, NoRoute}) -> Route ++ NoRoute.
-
-filter(Modules) ->
- [M || M <- Modules, code:which(M) =/= non_existing].
-
-set(X) ->
- Decs = lists:foldl(fun (D, {Route, NoRoute}) ->
- ActiveFor = D:active_for(X),
- {cons_if_eq(all, ActiveFor, D, Route),
- cons_if_eq(noroute, ActiveFor, D, NoRoute)}
- end, {[], []}, list()),
- X#exchange{decorators = Decs}.
-
-list() -> [M || {_, M} <- rabbit_registry:lookup_all(exchange_decorator)].
-
-cons_if_eq(Select, Select, Item, List) -> [Item | List];
-cons_if_eq(_Select, _Other, _Item, List) -> List.
-
-register(TypeName, ModuleName) ->
- rabbit_registry:register(exchange_decorator, TypeName, ModuleName),
- [maybe_recover(X) || X <- rabbit_exchange:list()],
- ok.
-
-unregister(TypeName) ->
- rabbit_registry:unregister(exchange_decorator, TypeName),
- [maybe_recover(X) || X <- rabbit_exchange:list()],
- ok.
-
-maybe_recover(X = #exchange{name = Name,
- decorators = Decs}) ->
- #exchange{decorators = Decs1} = set(X),
- Old = lists:sort(select(all, Decs)),
- New = lists:sort(select(all, Decs1)),
- case New of
- Old -> ok;
- _ -> %% TODO create a tx here for non-federation decorators
- [M:create(none, X) || M <- New -- Old],
- rabbit_exchange:update_decorators(Name)
- end.
diff --git a/src/rabbit_exchange_type.erl b/src/rabbit_exchange_type.erl
deleted file mode 100644
index 92c1de6c21..0000000000
--- a/src/rabbit_exchange_type.erl
+++ /dev/null
@@ -1,81 +0,0 @@
-%% The contents of this file are subject to the Mozilla Public License
-%% Version 1.1 (the "License"); you may not use this file except in
-%% compliance with the License. You may obtain a copy of the License
-%% at http://www.mozilla.org/MPL/
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and
-%% limitations under the License.
-%%
-%% The Original Code is RabbitMQ.
-%%
-%% The Initial Developer of the Original Code is GoPivotal, Inc.
-%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved.
-%%
-
--module(rabbit_exchange_type).
-
--ifdef(use_specs).
-
--type(tx() :: 'transaction' | 'none').
--type(serial() :: pos_integer() | tx()).
-
--callback description() -> [proplists:property()].
-
-%% Should Rabbit ensure that all binding events that are
-%% delivered to an individual exchange can be serialised? (they
-%% might still be delivered out of order, but there'll be a
-%% serial number).
--callback serialise_events() -> boolean().
-
-%% The no_return is there so that we can have an "invalid" exchange
-%% type (see rabbit_exchange_type_invalid).
--callback route(rabbit_types:exchange(), rabbit_types:delivery()) ->
- rabbit_router:match_result().
-
-%% called BEFORE declaration, to check args etc; may exit with #amqp_error{}
--callback validate(rabbit_types:exchange()) -> 'ok'.
-
-%% called BEFORE declaration, to check args etc
--callback validate_binding(rabbit_types:exchange(), rabbit_types:binding()) ->
- rabbit_types:ok_or_error({'binding_invalid', string(), [any()]}).
-
-%% called after declaration and recovery
--callback create(tx(), rabbit_types:exchange()) -> 'ok'.
-
-%% called after exchange (auto)deletion.
--callback delete(tx(), rabbit_types:exchange(), [rabbit_types:binding()]) ->
- 'ok'.
-
-%% called when the policy attached to this exchange changes.
--callback policy_changed(rabbit_types:exchange(), rabbit_types:exchange()) ->
- 'ok'.
-
-%% called after a binding has been added or recovered
--callback add_binding(serial(), rabbit_types:exchange(),
- rabbit_types:binding()) -> 'ok'.
-
-%% called after bindings have been deleted.
--callback remove_bindings(serial(), rabbit_types:exchange(),
- [rabbit_types:binding()]) -> 'ok'.
-
-%% called when comparing exchanges for equivalence - should return ok or
-%% exit with #amqp_error{}
--callback assert_args_equivalence(rabbit_types:exchange(),
- rabbit_framing:amqp_table()) ->
- 'ok' | rabbit_types:connection_exit().
-
--else.
-
--export([behaviour_info/1]).
-
-behaviour_info(callbacks) ->
- [{description, 0}, {serialise_events, 0}, {route, 2},
- {validate, 1}, {validate_binding, 2}, {policy_changed, 2},
- {create, 2}, {delete, 3}, {add_binding, 3}, {remove_bindings, 3},
- {assert_args_equivalence, 2}];
-behaviour_info(_Other) ->
- undefined.
-
--endif.
diff --git a/src/rabbit_heartbeat.erl b/src/rabbit_heartbeat.erl
deleted file mode 100644
index 993076770f..0000000000
--- a/src/rabbit_heartbeat.erl
+++ /dev/null
@@ -1,166 +0,0 @@
-%% The contents of this file are subject to the Mozilla Public License
-%% Version 1.1 (the "License"); you may not use this file except in
-%% compliance with the License. You may obtain a copy of the License
-%% at http://www.mozilla.org/MPL/
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and
-%% limitations under the License.
-%%
-%% The Original Code is RabbitMQ.
-%%
-%% The Initial Developer of the Original Code is GoPivotal, Inc.
-%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved.
-%%
-
--module(rabbit_heartbeat).
-
--export([start/6, start/7]).
--export([start_heartbeat_sender/4, start_heartbeat_receiver/4,
- pause_monitor/1, resume_monitor/1]).
-
--export([system_continue/3, system_terminate/4, system_code_change/4]).
-
--include("rabbit.hrl").
-
-%%----------------------------------------------------------------------------
-
--ifdef(use_specs).
-
--export_type([heartbeaters/0]).
-
--type(heartbeaters() :: {rabbit_types:maybe(pid()), rabbit_types:maybe(pid())}).
-
--type(heartbeat_callback() :: fun (() -> any())).
-
--spec(start/6 ::
- (pid(), rabbit_net:socket(),
- non_neg_integer(), heartbeat_callback(),
- non_neg_integer(), heartbeat_callback()) -> heartbeaters()).
-
--spec(start/7 ::
- (pid(), rabbit_net:socket(), rabbit_types:proc_name(),
- non_neg_integer(), heartbeat_callback(),
- non_neg_integer(), heartbeat_callback()) -> heartbeaters()).
-
--spec(start_heartbeat_sender/4 ::
- (rabbit_net:socket(), non_neg_integer(), heartbeat_callback(),
- rabbit_types:proc_type_and_name()) -> rabbit_types:ok(pid())).
--spec(start_heartbeat_receiver/4 ::
- (rabbit_net:socket(), non_neg_integer(), heartbeat_callback(),
- rabbit_types:proc_type_and_name()) -> rabbit_types:ok(pid())).
-
--spec(pause_monitor/1 :: (heartbeaters()) -> 'ok').
--spec(resume_monitor/1 :: (heartbeaters()) -> 'ok').
-
--spec(system_code_change/4 :: (_,_,_,_) -> {'ok',_}).
--spec(system_continue/3 :: (_,_,{_, _}) -> any()).
--spec(system_terminate/4 :: (_,_,_,_) -> none()).
-
--endif.
-
-%%----------------------------------------------------------------------------
-start(SupPid, Sock, SendTimeoutSec, SendFun, ReceiveTimeoutSec, ReceiveFun) ->
- start(SupPid, Sock, unknown,
- SendTimeoutSec, SendFun, ReceiveTimeoutSec, ReceiveFun).
-
-start(SupPid, Sock, Identity,
- SendTimeoutSec, SendFun, ReceiveTimeoutSec, ReceiveFun) ->
- {ok, Sender} =
- start_heartbeater(SendTimeoutSec, SupPid, Sock,
- SendFun, heartbeat_sender,
- start_heartbeat_sender, Identity),
- {ok, Receiver} =
- start_heartbeater(ReceiveTimeoutSec, SupPid, Sock,
- ReceiveFun, heartbeat_receiver,
- start_heartbeat_receiver, Identity),
- {Sender, Receiver}.
-
-start_heartbeat_sender(Sock, TimeoutSec, SendFun, Identity) ->
- %% the 'div 2' is there so that we don't end up waiting for nearly
- %% 2 * TimeoutSec before sending a heartbeat in the boundary case
- %% where the last message was sent just after a heartbeat.
- heartbeater({Sock, TimeoutSec * 1000 div 2, send_oct, 0,
- fun () -> SendFun(), continue end}, Identity).
-
-start_heartbeat_receiver(Sock, TimeoutSec, ReceiveFun, Identity) ->
- %% we check for incoming data every interval, and time out after
- %% two checks with no change. As a result we will time out between
- %% 2 and 3 intervals after the last data has been received.
- heartbeater({Sock, TimeoutSec * 1000, recv_oct, 1,
- fun () -> ReceiveFun(), stop end}, Identity).
-
-pause_monitor({_Sender, none}) -> ok;
-pause_monitor({_Sender, Receiver}) -> Receiver ! pause, ok.
-
-resume_monitor({_Sender, none}) -> ok;
-resume_monitor({_Sender, Receiver}) -> Receiver ! resume, ok.
-
-system_continue(_Parent, Deb, {Params, State}) ->
- heartbeater(Params, Deb, State).
-
-system_terminate(Reason, _Parent, _Deb, _State) ->
- exit(Reason).
-
-system_code_change(Misc, _Module, _OldVsn, _Extra) ->
- {ok, Misc}.
-
-%%----------------------------------------------------------------------------
-start_heartbeater(0, _SupPid, _Sock, _TimeoutFun, _Name, _Callback,
- _Identity) ->
- {ok, none};
-start_heartbeater(TimeoutSec, SupPid, Sock, TimeoutFun, Name, Callback,
- Identity) ->
- supervisor2:start_child(
- SupPid, {Name,
- {rabbit_heartbeat, Callback,
- [Sock, TimeoutSec, TimeoutFun, {Name, Identity}]},
- transient, ?MAX_WAIT, worker, [rabbit_heartbeat]}).
-
-heartbeater(Params, Identity) ->
- Deb = sys:debug_options([]),
- {ok, proc_lib:spawn_link(fun () ->
- rabbit_misc:store_proc_name(Identity),
- heartbeater(Params, Deb, {0, 0})
- end)}.
-
-heartbeater({Sock, TimeoutMillisec, StatName, Threshold, Handler} = Params,
- Deb, {StatVal, SameCount} = State) ->
- Recurse = fun (State1) -> heartbeater(Params, Deb, State1) end,
- System = fun (From, Req) ->
- sys:handle_system_msg(
- Req, From, self(), ?MODULE, Deb, {Params, State})
- end,
- receive
- pause ->
- receive
- resume -> Recurse({0, 0});
- {system, From, Req} -> System(From, Req);
- Other -> exit({unexpected_message, Other})
- end;
- {system, From, Req} ->
- System(From, Req);
- Other ->
- exit({unexpected_message, Other})
- after TimeoutMillisec ->
- case rabbit_net:getstat(Sock, [StatName]) of
- {ok, [{StatName, NewStatVal}]} ->
- if NewStatVal =/= StatVal ->
- Recurse({NewStatVal, 0});
- SameCount < Threshold ->
- Recurse({NewStatVal, SameCount + 1});
- true ->
- case Handler() of
- stop -> ok;
- continue -> Recurse({NewStatVal, 0})
- end
- end;
- {error, einval} ->
- %% the socket is dead, most likely because the
- %% connection is being shut down -> terminate
- ok;
- {error, Reason} ->
- exit({cannot_get_socket_stats, Reason})
- end
- end.
diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl
deleted file mode 100644
index cfabf1ed5e..0000000000
--- a/src/rabbit_misc.erl
+++ /dev/null
@@ -1,1165 +0,0 @@
-%% The contents of this file are subject to the Mozilla Public License
-%% Version 1.1 (the "License"); you may not use this file except in
-%% compliance with the License. You may obtain a copy of the License
-%% at http://www.mozilla.org/MPL/
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and
-%% limitations under the License.
-%%
-%% The Original Code is RabbitMQ.
-%%
-%% The Initial Developer of the Original Code is GoPivotal, Inc.
-%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved.
-%%
-
--module(rabbit_misc).
--include("rabbit.hrl").
--include("rabbit_framing.hrl").
-
--export([method_record_type/1, polite_pause/0, polite_pause/1]).
--export([die/1, frame_error/2, amqp_error/4, quit/1,
- protocol_error/3, protocol_error/4, protocol_error/1]).
--export([not_found/1, absent/2]).
--export([type_class/1, assert_args_equivalence/4, assert_field_equivalence/4]).
--export([dirty_read/1]).
--export([table_lookup/2, set_table_value/4]).
--export([r/3, r/2, r_arg/4, rs/1]).
--export([enable_cover/0, report_cover/0]).
--export([enable_cover/1, report_cover/1]).
--export([start_cover/1]).
--export([confirm_to_sender/2]).
--export([throw_on_error/2, with_exit_handler/2, is_abnormal_exit/1,
- filter_exit_map/2]).
--export([with_user/2, with_user_and_vhost/3]).
--export([execute_mnesia_transaction/1]).
--export([execute_mnesia_transaction/2]).
--export([execute_mnesia_tx_with_tail/1]).
--export([ensure_ok/2]).
--export([tcp_name/3, format_inet_error/1]).
--export([upmap/2, map_in_order/2]).
--export([table_filter/3]).
--export([dirty_read_all/1, dirty_foreach_key/2, dirty_dump_log/1]).
--export([format/2, format_many/1, format_stderr/2]).
--export([unfold/2, ceil/1, queue_fold/3]).
--export([sort_field_table/1]).
--export([pid_to_string/1, string_to_pid/1,
- pid_change_node/2, node_to_fake_pid/1]).
--export([version_compare/2, version_compare/3]).
--export([version_minor_equivalent/2]).
--export([dict_cons/3, orddict_cons/3, gb_trees_cons/3]).
--export([gb_trees_fold/3, gb_trees_foreach/2]).
--export([all_module_attributes/1, build_acyclic_graph/3]).
--export([const/1]).
--export([ntoa/1, ntoab/1]).
--export([is_process_alive/1]).
--export([pget/2, pget/3, pget_or_die/2, pmerge/3, pset/3, plmerge/2]).
--export([format_message_queue/2]).
--export([append_rpc_all_nodes/4]).
--export([os_cmd/1]).
--export([is_os_process_alive/1]).
--export([gb_sets_difference/2]).
--export([version/0, otp_release/0, which_applications/0]).
--export([sequence_error/1]).
--export([json_encode/1, json_decode/1, json_to_term/1, term_to_json/1]).
--export([check_expiry/1]).
--export([base64url/1]).
--export([interval_operation/5]).
--export([ensure_timer/4, stop_timer/2, send_after/3, cancel_timer/1]).
--export([get_parent/0]).
--export([store_proc_name/1, store_proc_name/2]).
--export([moving_average/4]).
--export([get_env/3]).
-
-%% Horrible macro to use in guards
--define(IS_BENIGN_EXIT(R),
- R =:= noproc; R =:= noconnection; R =:= nodedown; R =:= normal;
- R =:= shutdown).
-
-%%----------------------------------------------------------------------------
-
--ifdef(use_specs).
-
--export_type([resource_name/0, thunk/1, channel_or_connection_exit/0]).
-
--type(ok_or_error() :: rabbit_types:ok_or_error(any())).
--type(thunk(T) :: fun(() -> T)).
--type(resource_name() :: binary()).
--type(channel_or_connection_exit()
- :: rabbit_types:channel_exit() | rabbit_types:connection_exit()).
--type(digraph_label() :: term()).
--type(graph_vertex_fun() ::
- fun (({atom(), [term()]}) -> [{digraph:vertex(), digraph_label()}])).
--type(graph_edge_fun() ::
- fun (({atom(), [term()]}) -> [{digraph:vertex(), digraph:vertex()}])).
--type(tref() :: {'erlang', reference()} | {timer, timer:tref()}).
-
--spec(method_record_type/1 :: (rabbit_framing:amqp_method_record())
- -> rabbit_framing:amqp_method_name()).
--spec(polite_pause/0 :: () -> 'done').
--spec(polite_pause/1 :: (non_neg_integer()) -> 'done').
--spec(die/1 ::
- (rabbit_framing:amqp_exception()) -> channel_or_connection_exit()).
-
--spec(quit/1 :: (integer()) -> no_return()).
-
--spec(frame_error/2 :: (rabbit_framing:amqp_method_name(), binary())
- -> rabbit_types:connection_exit()).
--spec(amqp_error/4 ::
- (rabbit_framing:amqp_exception(), string(), [any()],
- rabbit_framing:amqp_method_name())
- -> rabbit_types:amqp_error()).
--spec(protocol_error/3 :: (rabbit_framing:amqp_exception(), string(), [any()])
- -> channel_or_connection_exit()).
--spec(protocol_error/4 ::
- (rabbit_framing:amqp_exception(), string(), [any()],
- rabbit_framing:amqp_method_name()) -> channel_or_connection_exit()).
--spec(protocol_error/1 ::
- (rabbit_types:amqp_error()) -> channel_or_connection_exit()).
--spec(not_found/1 :: (rabbit_types:r(atom())) -> rabbit_types:channel_exit()).
--spec(absent/2 :: (rabbit_types:amqqueue(), rabbit_amqqueue:absent_reason())
- -> rabbit_types:channel_exit()).
--spec(type_class/1 :: (rabbit_framing:amqp_field_type()) -> atom()).
--spec(assert_args_equivalence/4 :: (rabbit_framing:amqp_table(),
- rabbit_framing:amqp_table(),
- rabbit_types:r(any()), [binary()]) ->
- 'ok' | rabbit_types:connection_exit()).
--spec(assert_field_equivalence/4 ::
- (any(), any(), rabbit_types:r(any()), atom() | binary()) ->
- 'ok' | rabbit_types:connection_exit()).
--spec(equivalence_fail/4 ::
- (any(), any(), rabbit_types:r(any()), atom() | binary()) ->
- rabbit_types:connection_exit()).
--spec(dirty_read/1 ::
- ({atom(), any()}) -> rabbit_types:ok_or_error2(any(), 'not_found')).
--spec(table_lookup/2 ::
- (rabbit_framing:amqp_table(), binary())
- -> 'undefined' | {rabbit_framing:amqp_field_type(), any()}).
--spec(set_table_value/4 ::
- (rabbit_framing:amqp_table(), binary(),
- rabbit_framing:amqp_field_type(), rabbit_framing:amqp_value())
- -> rabbit_framing:amqp_table()).
--spec(r/2 :: (rabbit_types:vhost(), K)
- -> rabbit_types:r3(rabbit_types:vhost(), K, '_')
- when is_subtype(K, atom())).
--spec(r/3 ::
- (rabbit_types:vhost() | rabbit_types:r(atom()), K, resource_name())
- -> rabbit_types:r3(rabbit_types:vhost(), K, resource_name())
- when is_subtype(K, atom())).
--spec(r_arg/4 ::
- (rabbit_types:vhost() | rabbit_types:r(atom()), K,
- rabbit_framing:amqp_table(), binary()) ->
- undefined |
- rabbit_types:error(
- {invalid_type, rabbit_framing:amqp_field_type()}) |
- rabbit_types:r(K) when is_subtype(K, atom())).
--spec(rs/1 :: (rabbit_types:r(atom())) -> string()).
--spec(enable_cover/0 :: () -> ok_or_error()).
--spec(start_cover/1 :: ([{string(), string()} | string()]) -> 'ok').
--spec(report_cover/0 :: () -> 'ok').
--spec(enable_cover/1 :: ([file:filename() | atom()]) -> ok_or_error()).
--spec(report_cover/1 :: ([file:filename() | atom()]) -> 'ok').
--spec(throw_on_error/2 ::
- (atom(), thunk(rabbit_types:error(any()) | {ok, A} | A)) -> A).
--spec(with_exit_handler/2 :: (thunk(A), thunk(A)) -> A).
--spec(is_abnormal_exit/1 :: (any()) -> boolean()).
--spec(filter_exit_map/2 :: (fun ((A) -> B), [A]) -> [B]).
--spec(with_user/2 :: (rabbit_types:username(), thunk(A)) -> A).
--spec(with_user_and_vhost/3 ::
- (rabbit_types:username(), rabbit_types:vhost(), thunk(A))
- -> A).
--spec(execute_mnesia_transaction/1 :: (thunk(A)) -> A).
--spec(execute_mnesia_transaction/2 ::
- (thunk(A), fun ((A, boolean()) -> B)) -> B).
--spec(execute_mnesia_tx_with_tail/1 ::
- (thunk(fun ((boolean()) -> B))) -> B | (fun ((boolean()) -> B))).
--spec(ensure_ok/2 :: (ok_or_error(), atom()) -> 'ok').
--spec(tcp_name/3 ::
- (atom(), inet:ip_address(), rabbit_networking:ip_port())
- -> atom()).
--spec(format_inet_error/1 :: (atom()) -> string()).
--spec(upmap/2 :: (fun ((A) -> B), [A]) -> [B]).
--spec(map_in_order/2 :: (fun ((A) -> B), [A]) -> [B]).
--spec(table_filter/3:: (fun ((A) -> boolean()), fun ((A, boolean()) -> 'ok'),
- atom()) -> [A]).
--spec(dirty_read_all/1 :: (atom()) -> [any()]).
--spec(dirty_foreach_key/2 :: (fun ((any()) -> any()), atom())
- -> 'ok' | 'aborted').
--spec(dirty_dump_log/1 :: (file:filename()) -> ok_or_error()).
--spec(format/2 :: (string(), [any()]) -> string()).
--spec(format_many/1 :: ([{string(), [any()]}]) -> string()).
--spec(format_stderr/2 :: (string(), [any()]) -> 'ok').
--spec(unfold/2 :: (fun ((A) -> ({'true', B, A} | 'false')), A) -> {[B], A}).
--spec(ceil/1 :: (number()) -> integer()).
--spec(queue_fold/3 :: (fun ((any(), B) -> B), B, queue:queue()) -> B).
--spec(sort_field_table/1 ::
- (rabbit_framing:amqp_table()) -> rabbit_framing:amqp_table()).
--spec(pid_to_string/1 :: (pid()) -> string()).
--spec(string_to_pid/1 :: (string()) -> pid()).
--spec(pid_change_node/2 :: (pid(), node()) -> pid()).
--spec(node_to_fake_pid/1 :: (atom()) -> pid()).
--spec(version_compare/2 :: (string(), string()) -> 'lt' | 'eq' | 'gt').
--spec(version_compare/3 ::
- (string(), string(), ('lt' | 'lte' | 'eq' | 'gte' | 'gt'))
- -> boolean()).
--spec(version_minor_equivalent/2 :: (string(), string()) -> boolean()).
--spec(dict_cons/3 :: (any(), any(), dict:dict()) -> dict:dict()).
--spec(orddict_cons/3 :: (any(), any(), orddict:orddict()) -> orddict:orddict()).
--spec(gb_trees_cons/3 :: (any(), any(), gb_trees:tree()) -> gb_trees:tree()).
--spec(gb_trees_fold/3 :: (fun ((any(), any(), A) -> A), A, gb_trees:tree())
- -> A).
--spec(gb_trees_foreach/2 ::
- (fun ((any(), any()) -> any()), gb_trees:tree()) -> 'ok').
--spec(all_module_attributes/1 ::
- (atom()) -> [{atom(), atom(), [term()]}]).
--spec(build_acyclic_graph/3 ::
- (graph_vertex_fun(), graph_edge_fun(), [{atom(), [term()]}])
- -> rabbit_types:ok_or_error2(digraph:graph(),
- {'vertex', 'duplicate', digraph:vertex()} |
- {'edge', ({bad_vertex, digraph:vertex()} |
- {bad_edge, [digraph:vertex()]}),
- digraph:vertex(), digraph:vertex()})).
--spec(const/1 :: (A) -> thunk(A)).
--spec(ntoa/1 :: (inet:ip_address()) -> string()).
--spec(ntoab/1 :: (inet:ip_address()) -> string()).
--spec(is_process_alive/1 :: (pid()) -> boolean()).
--spec(pget/2 :: (term(), [term()]) -> term()).
--spec(pget/3 :: (term(), [term()], term()) -> term()).
--spec(pget_or_die/2 :: (term(), [term()]) -> term() | no_return()).
--spec(pmerge/3 :: (term(), term(), [term()]) -> [term()]).
--spec(plmerge/2 :: ([term()], [term()]) -> [term()]).
--spec(pset/3 :: (term(), term(), [term()]) -> [term()]).
--spec(format_message_queue/2 :: (any(), priority_queue:q()) -> term()).
--spec(append_rpc_all_nodes/4 :: ([node()], atom(), atom(), [any()]) -> [any()]).
--spec(os_cmd/1 :: (string()) -> string()).
--spec(is_os_process_alive/1 :: (non_neg_integer()) -> boolean()).
--spec(gb_sets_difference/2 :: (gb_sets:set(), gb_sets:set()) -> gb_sets:set()).
--spec(version/0 :: () -> string()).
--spec(otp_release/0 :: () -> string()).
--spec(which_applications/0 :: () -> [{atom(), string(), string()}]).
--spec(sequence_error/1 :: ([({'error', any()} | any())])
- -> {'error', any()} | any()).
--spec(json_encode/1 :: (any()) -> {'ok', string()} | {'error', any()}).
--spec(json_decode/1 :: (string()) -> {'ok', any()} | 'error').
--spec(json_to_term/1 :: (any()) -> any()).
--spec(term_to_json/1 :: (any()) -> any()).
--spec(check_expiry/1 :: (integer()) -> rabbit_types:ok_or_error(any())).
--spec(base64url/1 :: (binary()) -> string()).
--spec(interval_operation/5 ::
- ({atom(), atom(), any()}, float(), non_neg_integer(), non_neg_integer(), non_neg_integer())
- -> {any(), non_neg_integer()}).
--spec(ensure_timer/4 :: (A, non_neg_integer(), non_neg_integer(), any()) -> A).
--spec(stop_timer/2 :: (A, non_neg_integer()) -> A).
--spec(send_after/3 :: (non_neg_integer(), pid(), any()) -> tref()).
--spec(cancel_timer/1 :: (tref()) -> 'ok').
--spec(get_parent/0 :: () -> pid()).
--spec(store_proc_name/2 :: (atom(), rabbit_types:proc_name()) -> ok).
--spec(store_proc_name/1 :: (rabbit_types:proc_type_and_name()) -> ok).
--spec(moving_average/4 :: (float(), float(), float(), float() | 'undefined')
- -> float()).
--spec(get_env/3 :: (atom(), atom(), term()) -> term()).
--endif.
-
-%%----------------------------------------------------------------------------
-
-method_record_type(Record) ->
- element(1, Record).
-
-polite_pause() ->
- polite_pause(3000).
-
-polite_pause(N) ->
- receive
- after N -> done
- end.
-
-die(Error) ->
- protocol_error(Error, "~w", [Error]).
-
-frame_error(MethodName, BinaryFields) ->
- protocol_error(frame_error, "cannot decode ~w", [BinaryFields], MethodName).
-
-amqp_error(Name, ExplanationFormat, Params, Method) ->
- Explanation = format(ExplanationFormat, Params),
- #amqp_error{name = Name, explanation = Explanation, method = Method}.
-
-protocol_error(Name, ExplanationFormat, Params) ->
- protocol_error(Name, ExplanationFormat, Params, none).
-
-protocol_error(Name, ExplanationFormat, Params, Method) ->
- protocol_error(amqp_error(Name, ExplanationFormat, Params, Method)).
-
-protocol_error(#amqp_error{} = Error) ->
- exit(Error).
-
-not_found(R) -> protocol_error(not_found, "no ~s", [rs(R)]).
-
-absent(#amqqueue{name = QueueName, pid = QPid, durable = true}, nodedown) ->
- %% The assertion of durability is mainly there because we mention
- %% durability in the error message. That way we will hopefully
- %% notice if at some future point our logic changes s.t. we get
- %% here with non-durable queues.
- protocol_error(not_found,
- "home node '~s' of durable ~s is down or inaccessible",
- [node(QPid), rs(QueueName)]);
-
-absent(#amqqueue{name = QueueName}, crashed) ->
- protocol_error(not_found,
- "~s has crashed and failed to restart", [rs(QueueName)]).
-
-type_class(byte) -> int;
-type_class(short) -> int;
-type_class(signedint) -> int;
-type_class(long) -> int;
-type_class(decimal) -> int;
-type_class(float) -> float;
-type_class(double) -> float;
-type_class(Other) -> Other.
-
-assert_args_equivalence(Orig, New, Name, Keys) ->
- [assert_args_equivalence1(Orig, New, Name, Key) || Key <- Keys],
- ok.
-
-assert_args_equivalence1(Orig, New, Name, Key) ->
- {Orig1, New1} = {table_lookup(Orig, Key), table_lookup(New, Key)},
- case {Orig1, New1} of
- {Same, Same} ->
- ok;
- {{OrigType, OrigVal}, {NewType, NewVal}} ->
- case type_class(OrigType) == type_class(NewType) andalso
- OrigVal == NewVal of
- true -> ok;
- false -> assert_field_equivalence(OrigVal, NewVal, Name, Key)
- end;
- {OrigTypeVal, NewTypeVal} ->
- assert_field_equivalence(OrigTypeVal, NewTypeVal, Name, Key)
- end.
-
-assert_field_equivalence(_Orig, _Orig, _Name, _Key) ->
- ok;
-assert_field_equivalence(Orig, New, Name, Key) ->
- equivalence_fail(Orig, New, Name, Key).
-
-equivalence_fail(Orig, New, Name, Key) ->
- protocol_error(precondition_failed, "inequivalent arg '~s' "
- "for ~s: received ~s but current is ~s",
- [Key, rs(Name), val(New), val(Orig)]).
-
-val(undefined) ->
- "none";
-val({Type, Value}) ->
- ValFmt = case is_binary(Value) of
- true -> "~s";
- false -> "~p"
- end,
- format("the value '" ++ ValFmt ++ "' of type '~s'", [Value, Type]);
-val(Value) ->
- format(case is_binary(Value) of
- true -> "'~s'";
- false -> "'~p'"
- end, [Value]).
-
-%% Normally we'd call mnesia:dirty_read/1 here, but that is quite
-%% expensive due to general mnesia overheads (figuring out table types
-%% and locations, etc). We get away with bypassing these because we
-%% know that the tables we are looking at here
-%% - are not the schema table
-%% - have a local ram copy
-%% - do not have any indices
-dirty_read({Table, Key}) ->
- case ets:lookup(Table, Key) of
- [Result] -> {ok, Result};
- [] -> {error, not_found}
- end.
-
-table_lookup(Table, Key) ->
- case lists:keysearch(Key, 1, Table) of
- {value, {_, TypeBin, ValueBin}} -> {TypeBin, ValueBin};
- false -> undefined
- end.
-
-set_table_value(Table, Key, Type, Value) ->
- sort_field_table(
- lists:keystore(Key, 1, Table, {Key, Type, Value})).
-
-r(#resource{virtual_host = VHostPath}, Kind, Name) ->
- #resource{virtual_host = VHostPath, kind = Kind, name = Name};
-r(VHostPath, Kind, Name) ->
- #resource{virtual_host = VHostPath, kind = Kind, name = Name}.
-
-r(VHostPath, Kind) ->
- #resource{virtual_host = VHostPath, kind = Kind, name = '_'}.
-
-r_arg(#resource{virtual_host = VHostPath}, Kind, Table, Key) ->
- r_arg(VHostPath, Kind, Table, Key);
-r_arg(VHostPath, Kind, Table, Key) ->
- case table_lookup(Table, Key) of
- {longstr, NameBin} -> r(VHostPath, Kind, NameBin);
- undefined -> undefined;
- {Type, _} -> {error, {invalid_type, Type}}
- end.
-
-rs(#resource{virtual_host = VHostPath, kind = Kind, name = Name}) ->
- format("~s '~s' in vhost '~s'", [Kind, Name, VHostPath]).
-
-enable_cover() -> enable_cover(["."]).
-
-enable_cover(Dirs) ->
- lists:foldl(fun (Dir, ok) ->
- case cover:compile_beam_directory(
- filename:join(lists:concat([Dir]),"ebin")) of
- {error, _} = Err -> Err;
- _ -> ok
- end;
- (_Dir, Err) ->
- Err
- end, ok, Dirs).
-
-start_cover(NodesS) ->
- {ok, _} = cover:start([rabbit_nodes:make(N) || N <- NodesS]),
- ok.
-
-report_cover() -> report_cover(["."]).
-
-report_cover(Dirs) -> [report_cover1(lists:concat([Dir])) || Dir <- Dirs], ok.
-
-report_cover1(Root) ->
- Dir = filename:join(Root, "cover"),
- ok = filelib:ensure_dir(filename:join(Dir, "junk")),
- lists:foreach(fun (F) -> file:delete(F) end,
- filelib:wildcard(filename:join(Dir, "*.html"))),
- {ok, SummaryFile} = file:open(filename:join(Dir, "summary.txt"), [write]),
- {CT, NCT} =
- lists:foldl(
- fun (M,{CovTot, NotCovTot}) ->
- {ok, {M, {Cov, NotCov}}} = cover:analyze(M, module),
- ok = report_coverage_percentage(SummaryFile,
- Cov, NotCov, M),
- {ok,_} = cover:analyze_to_file(
- M,
- filename:join(Dir, atom_to_list(M) ++ ".html"),
- [html]),
- {CovTot+Cov, NotCovTot+NotCov}
- end,
- {0, 0},
- lists:sort(cover:modules())),
- ok = report_coverage_percentage(SummaryFile, CT, NCT, 'TOTAL'),
- ok = file:close(SummaryFile),
- ok.
-
-report_coverage_percentage(File, Cov, NotCov, Mod) ->
- io:fwrite(File, "~6.2f ~p~n",
- [if
- Cov+NotCov > 0 -> 100.0*Cov/(Cov+NotCov);
- true -> 100.0
- end,
- Mod]).
-
-confirm_to_sender(Pid, MsgSeqNos) ->
- gen_server2:cast(Pid, {confirm, MsgSeqNos, self()}).
-
-%% @doc Halts the emulator returning the given status code to the os.
-%% On Windows this function will block indefinitely so as to give the io
-%% subsystem time to flush stdout completely.
-quit(Status) ->
- case os:type() of
- {unix, _} -> halt(Status);
- {win32, _} -> init:stop(Status),
- receive
- after infinity -> ok
- end
- end.
-
-throw_on_error(E, Thunk) ->
- case Thunk() of
- {error, Reason} -> throw({E, Reason});
- {ok, Res} -> Res;
- Res -> Res
- end.
-
-with_exit_handler(Handler, Thunk) ->
- try
- Thunk()
- catch
- exit:{R, _} when ?IS_BENIGN_EXIT(R) -> Handler();
- exit:{{R, _}, _} when ?IS_BENIGN_EXIT(R) -> Handler()
- end.
-
-is_abnormal_exit(R) when ?IS_BENIGN_EXIT(R) -> false;
-is_abnormal_exit({R, _}) when ?IS_BENIGN_EXIT(R) -> false;
-is_abnormal_exit(_) -> true.
-
-filter_exit_map(F, L) ->
- Ref = make_ref(),
- lists:filter(fun (R) -> R =/= Ref end,
- [with_exit_handler(
- fun () -> Ref end,
- fun () -> F(I) end) || I <- L]).
-
-
-with_user(Username, Thunk) ->
- fun () ->
- case mnesia:read({rabbit_user, Username}) of
- [] ->
- mnesia:abort({no_such_user, Username});
- [_U] ->
- Thunk()
- end
- end.
-
-with_user_and_vhost(Username, VHostPath, Thunk) ->
- with_user(Username, rabbit_vhost:with(VHostPath, Thunk)).
-
-execute_mnesia_transaction(TxFun) ->
- %% Making this a sync_transaction allows us to use dirty_read
- %% elsewhere and get a consistent result even when that read
- %% executes on a different node.
- case worker_pool:submit(
- fun () ->
- case mnesia:is_transaction() of
- false -> DiskLogBefore = mnesia_dumper:get_log_writes(),
- Res = mnesia:sync_transaction(TxFun),
- DiskLogAfter = mnesia_dumper:get_log_writes(),
- case DiskLogAfter == DiskLogBefore of
- true -> file_handle_cache_stats:update(
- mnesia_ram_tx),
- Res;
- false -> file_handle_cache_stats:update(
- mnesia_disk_tx),
- {sync, Res}
- end;
- true -> mnesia:sync_transaction(TxFun)
- end
- end, single) of
- {sync, {atomic, Result}} -> mnesia_sync:sync(), Result;
- {sync, {aborted, Reason}} -> throw({error, Reason});
- {atomic, Result} -> Result;
- {aborted, Reason} -> throw({error, Reason})
- end.
-
-%% Like execute_mnesia_transaction/1 with additional Pre- and Post-
-%% commit function
-execute_mnesia_transaction(TxFun, PrePostCommitFun) ->
- case mnesia:is_transaction() of
- true -> throw(unexpected_transaction);
- false -> ok
- end,
- PrePostCommitFun(execute_mnesia_transaction(
- fun () ->
- Result = TxFun(),
- PrePostCommitFun(Result, true),
- Result
- end), false).
-
-%% Like execute_mnesia_transaction/2, but TxFun is expected to return a
-%% TailFun which gets called (only) immediately after the tx commit
-execute_mnesia_tx_with_tail(TxFun) ->
- case mnesia:is_transaction() of
- true -> execute_mnesia_transaction(TxFun);
- false -> TailFun = execute_mnesia_transaction(TxFun),
- TailFun()
- end.
-
-ensure_ok(ok, _) -> ok;
-ensure_ok({error, Reason}, ErrorTag) -> throw({error, {ErrorTag, Reason}}).
-
-tcp_name(Prefix, IPAddress, Port)
- when is_atom(Prefix) andalso is_number(Port) ->
- list_to_atom(
- format("~w_~s:~w", [Prefix, inet_parse:ntoa(IPAddress), Port])).
-
-format_inet_error(E) -> format("~w (~s)", [E, format_inet_error0(E)]).
-
-format_inet_error0(address) -> "cannot connect to host/port";
-format_inet_error0(timeout) -> "timed out";
-format_inet_error0(Error) -> inet:format_error(Error).
-
-%% This is a modified version of Luke Gorrie's pmap -
-%% http://lukego.livejournal.com/6753.html - that doesn't care about
-%% the order in which results are received.
-%%
-%% WARNING: This is is deliberately lightweight rather than robust -- if F
-%% throws, upmap will hang forever, so make sure F doesn't throw!
-upmap(F, L) ->
- Parent = self(),
- Ref = make_ref(),
- [receive {Ref, Result} -> Result end
- || _ <- [spawn(fun () -> Parent ! {Ref, F(X)} end) || X <- L]].
-
-map_in_order(F, L) ->
- lists:reverse(
- lists:foldl(fun (E, Acc) -> [F(E) | Acc] end, [], L)).
-
-%% Apply a pre-post-commit function to all entries in a table that
-%% satisfy a predicate, and return those entries.
-%%
-%% We ignore entries that have been modified or removed.
-table_filter(Pred, PrePostCommitFun, TableName) ->
- lists:foldl(
- fun (E, Acc) ->
- case execute_mnesia_transaction(
- fun () -> mnesia:match_object(TableName, E, read) =/= []
- andalso Pred(E) end,
- fun (false, _Tx) -> false;
- (true, Tx) -> PrePostCommitFun(E, Tx), true
- end) of
- false -> Acc;
- true -> [E | Acc]
- end
- end, [], dirty_read_all(TableName)).
-
-dirty_read_all(TableName) ->
- mnesia:dirty_select(TableName, [{'$1',[],['$1']}]).
-
-dirty_foreach_key(F, TableName) ->
- dirty_foreach_key1(F, TableName, mnesia:dirty_first(TableName)).
-
-dirty_foreach_key1(_F, _TableName, '$end_of_table') ->
- ok;
-dirty_foreach_key1(F, TableName, K) ->
- case catch mnesia:dirty_next(TableName, K) of
- {'EXIT', _} ->
- aborted;
- NextKey ->
- F(K),
- dirty_foreach_key1(F, TableName, NextKey)
- end.
-
-dirty_dump_log(FileName) ->
- {ok, LH} = disk_log:open([{name, dirty_dump_log},
- {mode, read_only},
- {file, FileName}]),
- dirty_dump_log1(LH, disk_log:chunk(LH, start)),
- disk_log:close(LH).
-
-dirty_dump_log1(_LH, eof) ->
- io:format("Done.~n");
-dirty_dump_log1(LH, {K, Terms}) ->
- io:format("Chunk: ~p~n", [Terms]),
- dirty_dump_log1(LH, disk_log:chunk(LH, K));
-dirty_dump_log1(LH, {K, Terms, BadBytes}) ->
- io:format("Bad Chunk, ~p: ~p~n", [BadBytes, Terms]),
- dirty_dump_log1(LH, disk_log:chunk(LH, K)).
-
-format(Fmt, Args) -> lists:flatten(io_lib:format(Fmt, Args)).
-
-format_many(List) ->
- lists:flatten([io_lib:format(F ++ "~n", A) || {F, A} <- List]).
-
-format_stderr(Fmt, Args) ->
- case os:type() of
- {unix, _} ->
- Port = open_port({fd, 0, 2}, [out]),
- port_command(Port, io_lib:format(Fmt, Args)),
- port_close(Port);
- {win32, _} ->
- %% stderr on Windows is buffered and I can't figure out a
- %% way to trigger a fflush(stderr) in Erlang. So rather
- %% than risk losing output we write to stdout instead,
- %% which appears to be unbuffered.
- io:format(Fmt, Args)
- end,
- ok.
-
-unfold(Fun, Init) ->
- unfold(Fun, [], Init).
-
-unfold(Fun, Acc, Init) ->
- case Fun(Init) of
- {true, E, I} -> unfold(Fun, [E|Acc], I);
- false -> {Acc, Init}
- end.
-
-ceil(N) ->
- T = trunc(N),
- case N == T of
- true -> T;
- false -> 1 + T
- end.
-
-queue_fold(Fun, Init, Q) ->
- case queue:out(Q) of
- {empty, _Q} -> Init;
- {{value, V}, Q1} -> queue_fold(Fun, Fun(V, Init), Q1)
- end.
-
-%% Sorts a list of AMQP table fields as per the AMQP spec
-sort_field_table(Arguments) ->
- lists:keysort(1, Arguments).
-
-%% This provides a string representation of a pid that is the same
-%% regardless of what node we are running on. The representation also
-%% permits easy identification of the pid's node.
-pid_to_string(Pid) when is_pid(Pid) ->
- {Node, Cre, Id, Ser} = decompose_pid(Pid),
- format("<~s.~B.~B.~B>", [Node, Cre, Id, Ser]).
-
-%% inverse of above
-string_to_pid(Str) ->
- Err = {error, {invalid_pid_syntax, Str}},
- %% The \ before the trailing $ is only there to keep emacs
- %% font-lock from getting confused.
- case re:run(Str, "^<(.*)\\.(\\d+)\\.(\\d+)\\.(\\d+)>\$",
- [{capture,all_but_first,list}]) of
- {match, [NodeStr, CreStr, IdStr, SerStr]} ->
- [Cre, Id, Ser] = lists:map(fun list_to_integer/1,
- [CreStr, IdStr, SerStr]),
- compose_pid(list_to_atom(NodeStr), Cre, Id, Ser);
- nomatch ->
- throw(Err)
- end.
-
-pid_change_node(Pid, NewNode) ->
- {_OldNode, Cre, Id, Ser} = decompose_pid(Pid),
- compose_pid(NewNode, Cre, Id, Ser).
-
-%% node(node_to_fake_pid(Node)) =:= Node.
-node_to_fake_pid(Node) ->
- compose_pid(Node, 0, 0, 0).
-
-decompose_pid(Pid) when is_pid(Pid) ->
- %% see http://erlang.org/doc/apps/erts/erl_ext_dist.html (8.10 and
- %% 8.7)
- <<131,103,100,NodeLen:16,NodeBin:NodeLen/binary,Id:32,Ser:32,Cre:8>>
- = term_to_binary(Pid),
- Node = binary_to_term(<<131,100,NodeLen:16,NodeBin:NodeLen/binary>>),
- {Node, Cre, Id, Ser}.
-
-compose_pid(Node, Cre, Id, Ser) ->
- <<131,NodeEnc/binary>> = term_to_binary(Node),
- binary_to_term(<<131,103,NodeEnc/binary,Id:32,Ser:32,Cre:8>>).
-
-version_compare(A, B, lte) ->
- case version_compare(A, B) of
- eq -> true;
- lt -> true;
- gt -> false
- end;
-version_compare(A, B, gte) ->
- case version_compare(A, B) of
- eq -> true;
- gt -> true;
- lt -> false
- end;
-version_compare(A, B, Result) ->
- Result =:= version_compare(A, B).
-
-version_compare(A, A) ->
- eq;
-version_compare([], [$0 | B]) ->
- version_compare([], dropdot(B));
-version_compare([], _) ->
- lt; %% 2.3 < 2.3.1
-version_compare([$0 | A], []) ->
- version_compare(dropdot(A), []);
-version_compare(_, []) ->
- gt; %% 2.3.1 > 2.3
-version_compare(A, B) ->
- {AStr, ATl} = lists:splitwith(fun (X) -> X =/= $. end, A),
- {BStr, BTl} = lists:splitwith(fun (X) -> X =/= $. end, B),
- ANum = list_to_integer(AStr),
- BNum = list_to_integer(BStr),
- if ANum =:= BNum -> version_compare(dropdot(ATl), dropdot(BTl));
- ANum < BNum -> lt;
- ANum > BNum -> gt
- end.
-
-%% a.b.c and a.b.d match, but a.b.c and a.d.e don't. If
-%% versions do not match that pattern, just compare them.
-version_minor_equivalent(A, B) ->
- {ok, RE} = re:compile("^(\\d+\\.\\d+)(\\.\\d+)\$"),
- Opts = [{capture, all_but_first, list}],
- case {re:run(A, RE, Opts), re:run(B, RE, Opts)} of
- {{match, [A1|_]}, {match, [B1|_]}} -> A1 =:= B1;
- _ -> A =:= B
- end.
-
-dropdot(A) -> lists:dropwhile(fun (X) -> X =:= $. end, A).
-
-dict_cons(Key, Value, Dict) ->
- dict:update(Key, fun (List) -> [Value | List] end, [Value], Dict).
-
-orddict_cons(Key, Value, Dict) ->
- orddict:update(Key, fun (List) -> [Value | List] end, [Value], Dict).
-
-gb_trees_cons(Key, Value, Tree) ->
- case gb_trees:lookup(Key, Tree) of
- {value, Values} -> gb_trees:update(Key, [Value | Values], Tree);
- none -> gb_trees:insert(Key, [Value], Tree)
- end.
-
-gb_trees_fold(Fun, Acc, Tree) ->
- gb_trees_fold1(Fun, Acc, gb_trees:next(gb_trees:iterator(Tree))).
-
-gb_trees_fold1(_Fun, Acc, none) ->
- Acc;
-gb_trees_fold1(Fun, Acc, {Key, Val, It}) ->
- gb_trees_fold1(Fun, Fun(Key, Val, Acc), gb_trees:next(It)).
-
-gb_trees_foreach(Fun, Tree) ->
- gb_trees_fold(fun (Key, Val, Acc) -> Fun(Key, Val), Acc end, ok, Tree).
-
-module_attributes(Module) ->
- case catch Module:module_info(attributes) of
- {'EXIT', {undef, [{Module, module_info, _} | _]}} ->
- io:format("WARNING: module ~p not found, so not scanned for boot steps.~n",
- [Module]),
- [];
- {'EXIT', Reason} ->
- exit(Reason);
- V ->
- V
- end.
-
-all_module_attributes(Name) ->
- Targets =
- lists:usort(
- lists:append(
- [[{App, Module} || Module <- Modules] ||
- {App, _, _} <- application:loaded_applications(),
- {ok, Modules} <- [application:get_key(App, modules)]])),
- lists:foldl(
- fun ({App, Module}, Acc) ->
- case lists:append([Atts || {N, Atts} <- module_attributes(Module),
- N =:= Name]) of
- [] -> Acc;
- Atts -> [{App, Module, Atts} | Acc]
- end
- end, [], Targets).
-
-build_acyclic_graph(VertexFun, EdgeFun, Graph) ->
- G = digraph:new([acyclic]),
- try
- [case digraph:vertex(G, Vertex) of
- false -> digraph:add_vertex(G, Vertex, Label);
- _ -> ok = throw({graph_error, {vertex, duplicate, Vertex}})
- end || GraphElem <- Graph,
- {Vertex, Label} <- VertexFun(GraphElem)],
- [case digraph:add_edge(G, From, To) of
- {error, E} -> throw({graph_error, {edge, E, From, To}});
- _ -> ok
- end || GraphElem <- Graph,
- {From, To} <- EdgeFun(GraphElem)],
- {ok, G}
- catch {graph_error, Reason} ->
- true = digraph:delete(G),
- {error, Reason}
- end.
-
-const(X) -> fun () -> X end.
-
-%% Format IPv4-mapped IPv6 addresses as IPv4, since they're what we see
-%% when IPv6 is enabled but not used (i.e. 99% of the time).
-ntoa({0,0,0,0,0,16#ffff,AB,CD}) ->
- inet_parse:ntoa({AB bsr 8, AB rem 256, CD bsr 8, CD rem 256});
-ntoa(IP) ->
- inet_parse:ntoa(IP).
-
-ntoab(IP) ->
- Str = ntoa(IP),
- case string:str(Str, ":") of
- 0 -> Str;
- _ -> "[" ++ Str ++ "]"
- end.
-
-%% We try to avoid reconnecting to down nodes here; this is used in a
-%% loop in rabbit_amqqueue:on_node_down/1 and any delays we incur
-%% would be bad news.
-%%
-%% See also rabbit_mnesia:is_process_alive/1 which also requires the
-%% process be in the same running cluster as us (i.e. not partitioned
-%% or some random node).
-is_process_alive(Pid) ->
- Node = node(Pid),
- lists:member(Node, [node() | nodes()]) andalso
- rpc:call(Node, erlang, is_process_alive, [Pid]) =:= true.
-
-pget(K, P) -> proplists:get_value(K, P).
-pget(K, P, D) -> proplists:get_value(K, P, D).
-
-pget_or_die(K, P) ->
- case proplists:get_value(K, P) of
- undefined -> exit({error, key_missing, K});
- V -> V
- end.
-
-%% property merge
-pmerge(Key, Val, List) ->
- case proplists:is_defined(Key, List) of
- true -> List;
- _ -> [{Key, Val} | List]
- end.
-
-%% proplists merge
-plmerge(P1, P2) ->
- dict:to_list(dict:merge(fun(_, V, _) ->
- V
- end,
- dict:from_list(P1),
- dict:from_list(P2))).
-
-pset(Key, Value, List) -> [{Key, Value} | proplists:delete(Key, List)].
-
-format_message_queue(_Opt, MQ) ->
- Len = priority_queue:len(MQ),
- {Len,
- case Len > 100 of
- false -> priority_queue:to_list(MQ);
- true -> {summary,
- orddict:to_list(
- lists:foldl(
- fun ({P, V}, Counts) ->
- orddict:update_counter(
- {P, format_message_queue_entry(V)}, 1, Counts)
- end, orddict:new(), priority_queue:to_list(MQ)))}
- end}.
-
-format_message_queue_entry(V) when is_atom(V) ->
- V;
-format_message_queue_entry(V) when is_tuple(V) ->
- list_to_tuple([format_message_queue_entry(E) || E <- tuple_to_list(V)]);
-format_message_queue_entry(_V) ->
- '_'.
-
-append_rpc_all_nodes(Nodes, M, F, A) ->
- {ResL, _} = rpc:multicall(Nodes, M, F, A),
- lists:append([case Res of
- {badrpc, _} -> [];
- _ -> Res
- end || Res <- ResL]).
-
-os_cmd(Command) ->
- case os:type() of
- {win32, _} ->
- %% Clink workaround; see
- %% http://code.google.com/p/clink/issues/detail?id=141
- os:cmd(" " ++ Command);
- _ ->
- %% Don't just return "/bin/sh: <cmd>: not found" if not found
- Exec = hd(string:tokens(Command, " ")),
- case os:find_executable(Exec) of
- false -> throw({command_not_found, Exec});
- _ -> os:cmd(Command)
- end
- end.
-
-is_os_process_alive(Pid) ->
- with_os([{unix, fun () ->
- run_ps(Pid) =:= 0
- end},
- {win32, fun () ->
- Cmd = "tasklist /nh /fi \"pid eq " ++ Pid ++ "\" ",
- Res = os_cmd(Cmd ++ "2>&1"),
- case re:run(Res, "erl\\.exe", [{capture, none}]) of
- match -> true;
- _ -> false
- end
- end}]).
-
-with_os(Handlers) ->
- {OsFamily, _} = os:type(),
- case proplists:get_value(OsFamily, Handlers) of
- undefined -> throw({unsupported_os, OsFamily});
- Handler -> Handler()
- end.
-
-run_ps(Pid) ->
- Port = erlang:open_port({spawn, "ps -p " ++ Pid},
- [exit_status, {line, 16384},
- use_stdio, stderr_to_stdout]),
- exit_loop(Port).
-
-exit_loop(Port) ->
- receive
- {Port, {exit_status, Rc}} -> Rc;
- {Port, _} -> exit_loop(Port)
- end.
-
-gb_sets_difference(S1, S2) ->
- gb_sets:fold(fun gb_sets:delete_any/2, S1, S2).
-
-version() ->
- {ok, VSN} = application:get_key(rabbit, vsn),
- VSN.
-
-%% See http://www.erlang.org/doc/system_principles/versions.html
-otp_release() ->
- File = filename:join([code:root_dir(), "releases",
- erlang:system_info(otp_release), "OTP_VERSION"]),
- case file:read_file(File) of
- {ok, VerBin} ->
- %% 17.0 or later, we need the file for the minor version
- string:strip(binary_to_list(VerBin), both, $\n);
- {error, _} ->
- %% R16B03 or earlier (no file, otp_release is correct)
- %% or we couldn't read the file (so this is best we can do)
- erlang:system_info(otp_release)
- end.
-
-%% application:which_applications(infinity) is dangerous, since it can
-%% cause deadlocks on shutdown. So we have to use a timeout variant,
-%% but w/o creating spurious timeout errors.
-which_applications() ->
- try
- application:which_applications()
- catch
- exit:{timeout, _} -> []
- end.
-
-sequence_error([T]) -> T;
-sequence_error([{error, _} = Error | _]) -> Error;
-sequence_error([_ | Rest]) -> sequence_error(Rest).
-
-json_encode(Term) ->
- try
- {ok, mochijson2:encode(Term)}
- catch
- exit:{json_encode, E} ->
- {error, E}
- end.
-
-json_decode(Term) ->
- try
- {ok, mochijson2:decode(Term)}
- catch
- %% Sadly `mochijson2:decode/1' does not offer a nice way to catch
- %% decoding errors...
- error:_ -> error
- end.
-
-json_to_term({struct, L}) ->
- [{K, json_to_term(V)} || {K, V} <- L];
-json_to_term(L) when is_list(L) ->
- [json_to_term(I) || I <- L];
-json_to_term(V) when is_binary(V) orelse is_number(V) orelse V =:= null orelse
- V =:= true orelse V =:= false ->
- V.
-
-%% This has the flaw that empty lists will never be JSON objects, so use with
-%% care.
-term_to_json([{_, _}|_] = L) ->
- {struct, [{K, term_to_json(V)} || {K, V} <- L]};
-term_to_json(L) when is_list(L) ->
- [term_to_json(I) || I <- L];
-term_to_json(V) when is_binary(V) orelse is_number(V) orelse V =:= null orelse
- V =:= true orelse V =:= false ->
- V.
-
-check_expiry(N) when N < 0 -> {error, {value_negative, N}};
-check_expiry(_N) -> ok.
-
-base64url(In) ->
- lists:reverse(lists:foldl(fun ($\+, Acc) -> [$\- | Acc];
- ($\/, Acc) -> [$\_ | Acc];
- ($\=, Acc) -> Acc;
- (Chr, Acc) -> [Chr | Acc]
- end, [], base64:encode_to_string(In))).
-
-%% Ideally, you'd want Fun to run every IdealInterval. but you don't
-%% want it to take more than MaxRatio of IdealInterval. So if it takes
-%% more then you want to run it less often. So we time how long it
-%% takes to run, and then suggest how long you should wait before
-%% running it again with a user specified max interval. Times are in millis.
-interval_operation({M, F, A}, MaxRatio, MaxInterval, IdealInterval, LastInterval) ->
- {Micros, Res} = timer:tc(M, F, A),
- {Res, case {Micros > 1000 * (MaxRatio * IdealInterval),
- Micros > 1000 * (MaxRatio * LastInterval)} of
- {true, true} -> lists:min([MaxInterval,
- round(LastInterval * 1.5)]);
- {true, false} -> LastInterval;
- {false, false} -> lists:max([IdealInterval,
- round(LastInterval / 1.5)])
- end}.
-
-ensure_timer(State, Idx, After, Msg) ->
- case element(Idx, State) of
- undefined -> TRef = send_after(After, self(), Msg),
- setelement(Idx, State, TRef);
- _ -> State
- end.
-
-stop_timer(State, Idx) ->
- case element(Idx, State) of
- undefined -> State;
- TRef -> cancel_timer(TRef),
- setelement(Idx, State, undefined)
- end.
-
-%% timer:send_after/3 goes through a single timer process but allows
-%% long delays. erlang:send_after/3 does not have a bottleneck but
-%% only allows max 2^32-1 millis.
--define(MAX_ERLANG_SEND_AFTER, 4294967295).
-send_after(Millis, Pid, Msg) when Millis > ?MAX_ERLANG_SEND_AFTER ->
- {ok, Ref} = timer:send_after(Millis, Pid, Msg),
- {timer, Ref};
-send_after(Millis, Pid, Msg) ->
- {erlang, erlang:send_after(Millis, Pid, Msg)}.
-
-cancel_timer({erlang, Ref}) -> erlang:cancel_timer(Ref),
- ok;
-cancel_timer({timer, Ref}) -> {ok, cancel} = timer:cancel(Ref),
- ok.
-
-store_proc_name(Type, ProcName) -> store_proc_name({Type, ProcName}).
-store_proc_name(TypeProcName) -> put(process_name, TypeProcName).
-
-%% application:get_env/3 is only available in R16B01 or later.
-get_env(Application, Key, Def) ->
- case application:get_env(Application, Key) of
- {ok, Val} -> Val;
- undefined -> Def
- end.
-
-moving_average(_Time, _HalfLife, Next, undefined) ->
- Next;
-%% We want the Weight to decrease as Time goes up (since Weight is the
-%% weight for the current sample, not the new one), so that the moving
-%% average decays at the same speed regardless of how long the time is
-%% between samplings. So we want Weight = math:exp(Something), where
-%% Something turns out to be negative.
-%%
-%% We want to determine Something here in terms of the Time taken
-%% since the last measurement, and a HalfLife. So we want Weight =
-%% math:exp(Time * Constant / HalfLife). What should Constant be? We
-%% want Weight to be 0.5 when Time = HalfLife.
-%%
-%% Plug those numbers in and you get 0.5 = math:exp(Constant). Take
-%% the log of each side and you get math:log(0.5) = Constant.
-moving_average(Time, HalfLife, Next, Current) ->
- Weight = math:exp(Time * math:log(0.5) / HalfLife),
- Next * (1 - Weight) + Current * Weight.
-
-%% -------------------------------------------------------------------------
-%% Begin copypasta from gen_server2.erl
-
-get_parent() ->
- case get('$ancestors') of
- [Parent | _] when is_pid (Parent) -> Parent;
- [Parent | _] when is_atom(Parent) -> name_to_pid(Parent);
- _ -> exit(process_was_not_started_by_proc_lib)
- end.
-
-name_to_pid(Name) ->
- case whereis(Name) of
- undefined -> case whereis_name(Name) of
- undefined -> exit(could_not_find_registerd_name);
- Pid -> Pid
- end;
- Pid -> Pid
- end.
-
-whereis_name(Name) ->
- case ets:lookup(global_names, Name) of
- [{_Name, Pid, _Method, _RPid, _Ref}] ->
- if node(Pid) == node() -> case erlang:is_process_alive(Pid) of
- true -> Pid;
- false -> undefined
- end;
- true -> Pid
- end;
- [] -> undefined
- end.
-
-%% End copypasta from gen_server2.erl
-%% -------------------------------------------------------------------------
diff --git a/src/rabbit_msg_store_index.erl b/src/rabbit_msg_store_index.erl
deleted file mode 100644
index 0c7a37bcd3..0000000000
--- a/src/rabbit_msg_store_index.erl
+++ /dev/null
@@ -1,59 +0,0 @@
-%% The contents of this file are subject to the Mozilla Public License
-%% Version 1.1 (the "License"); you may not use this file except in
-%% compliance with the License. You may obtain a copy of the License
-%% at http://www.mozilla.org/MPL/
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and
-%% limitations under the License.
-%%
-%% The Original Code is RabbitMQ.
-%%
-%% The Initial Developer of the Original Code is GoPivotal, Inc.
-%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved.
-%%
-
--module(rabbit_msg_store_index).
-
--include("rabbit_msg_store.hrl").
-
--ifdef(use_specs).
-
--type(dir() :: any()).
--type(index_state() :: any()).
--type(keyvalue() :: any()).
--type(fieldpos() :: non_neg_integer()).
--type(fieldvalue() :: any()).
-
--callback new(dir()) -> index_state().
--callback recover(dir()) -> rabbit_types:ok_or_error2(index_state(), any()).
--callback lookup(rabbit_types:msg_id(), index_state()) -> ('not_found' | keyvalue()).
--callback insert(keyvalue(), index_state()) -> 'ok'.
--callback update(keyvalue(), index_state()) -> 'ok'.
--callback update_fields(rabbit_types:msg_id(), ({fieldpos(), fieldvalue()} |
- [{fieldpos(), fieldvalue()}]),
- index_state()) -> 'ok'.
--callback delete(rabbit_types:msg_id(), index_state()) -> 'ok'.
--callback delete_object(keyvalue(), index_state()) -> 'ok'.
--callback delete_by_file(fieldvalue(), index_state()) -> 'ok'.
--callback terminate(index_state()) -> any().
-
--else.
-
--export([behaviour_info/1]).
-
-behaviour_info(callbacks) ->
- [{new, 1},
- {recover, 1},
- {lookup, 2},
- {insert, 2},
- {update, 2},
- {update_fields, 3},
- {delete, 2},
- {delete_by_file, 2},
- {terminate, 1}];
-behaviour_info(_Other) ->
- undefined.
-
--endif.
diff --git a/src/rabbit_net.erl b/src/rabbit_net.erl
deleted file mode 100644
index b6cf9842ff..0000000000
--- a/src/rabbit_net.erl
+++ /dev/null
@@ -1,246 +0,0 @@
-%% The contents of this file are subject to the Mozilla Public License
-%% Version 1.1 (the "License"); you may not use this file except in
-%% compliance with the License. You may obtain a copy of the License
-%% at http://www.mozilla.org/MPL/
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and
-%% limitations under the License.
-%%
-%% The Original Code is RabbitMQ.
-%%
-%% The Initial Developer of the Original Code is GoPivotal, Inc.
-%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved.
-%%
-
--module(rabbit_net).
--include("rabbit.hrl").
-
--export([is_ssl/1, ssl_info/1, controlling_process/2, getstat/2,
- recv/1, sync_recv/2, async_recv/3, port_command/2, getopts/2,
- setopts/2, send/2, close/1, fast_close/1, sockname/1, peername/1,
- peercert/1, connection_string/2, socket_ends/2, is_loopback/1]).
-
-%%---------------------------------------------------------------------------
-
--ifdef(use_specs).
-
--export_type([socket/0]).
-
--type(stat_option() ::
- 'recv_cnt' | 'recv_max' | 'recv_avg' | 'recv_oct' | 'recv_dvi' |
- 'send_cnt' | 'send_max' | 'send_avg' | 'send_oct' | 'send_pend').
--type(ok_val_or_error(A) :: rabbit_types:ok_or_error2(A, any())).
--type(ok_or_any_error() :: rabbit_types:ok_or_error(any())).
--type(socket() :: port() | #ssl_socket{}).
--type(opts() :: [{atom(), any()} |
- {raw, non_neg_integer(), non_neg_integer(), binary()}]).
--type(host_or_ip() :: binary() | inet:ip_address()).
--spec(is_ssl/1 :: (socket()) -> boolean()).
--spec(ssl_info/1 :: (socket())
- -> 'nossl' | ok_val_or_error(
- [{atom(), any()}])).
--spec(controlling_process/2 :: (socket(), pid()) -> ok_or_any_error()).
--spec(getstat/2 ::
- (socket(), [stat_option()])
- -> ok_val_or_error([{stat_option(), integer()}])).
--spec(recv/1 :: (socket()) ->
- {'data', [char()] | binary()} | 'closed' |
- rabbit_types:error(any()) | {'other', any()}).
--spec(sync_recv/2 :: (socket(), integer()) -> rabbit_types:ok(binary()) |
- rabbit_types:error(any())).
--spec(async_recv/3 ::
- (socket(), integer(), timeout()) -> rabbit_types:ok(any())).
--spec(port_command/2 :: (socket(), iolist()) -> 'true').
--spec(getopts/2 :: (socket(), [atom() | {raw,
- non_neg_integer(),
- non_neg_integer(),
- non_neg_integer() | binary()}])
- -> ok_val_or_error(opts())).
--spec(setopts/2 :: (socket(), opts()) -> ok_or_any_error()).
--spec(send/2 :: (socket(), binary() | iolist()) -> ok_or_any_error()).
--spec(close/1 :: (socket()) -> ok_or_any_error()).
--spec(fast_close/1 :: (socket()) -> ok_or_any_error()).
--spec(sockname/1 ::
- (socket())
- -> ok_val_or_error({inet:ip_address(), rabbit_networking:ip_port()})).
--spec(peername/1 ::
- (socket())
- -> ok_val_or_error({inet:ip_address(), rabbit_networking:ip_port()})).
--spec(peercert/1 ::
- (socket())
- -> 'nossl' | ok_val_or_error(rabbit_ssl:certificate())).
--spec(connection_string/2 ::
- (socket(), 'inbound' | 'outbound') -> ok_val_or_error(string())).
--spec(socket_ends/2 ::
- (socket(), 'inbound' | 'outbound')
- -> ok_val_or_error({host_or_ip(), rabbit_networking:ip_port(),
- host_or_ip(), rabbit_networking:ip_port()})).
--spec(is_loopback/1 :: (socket() | inet:ip_address()) -> boolean()).
-
--endif.
-
-%%---------------------------------------------------------------------------
-
--define(SSL_CLOSE_TIMEOUT, 5000).
-
--define(IS_SSL(Sock), is_record(Sock, ssl_socket)).
-
-is_ssl(Sock) -> ?IS_SSL(Sock).
-
-ssl_info(Sock) when ?IS_SSL(Sock) ->
- ssl_compat:connection_information(Sock#ssl_socket.ssl);
-ssl_info(_Sock) ->
- nossl.
-
-controlling_process(Sock, Pid) when ?IS_SSL(Sock) ->
- ssl:controlling_process(Sock#ssl_socket.ssl, Pid);
-controlling_process(Sock, Pid) when is_port(Sock) ->
- gen_tcp:controlling_process(Sock, Pid).
-
-getstat(Sock, Stats) when ?IS_SSL(Sock) ->
- inet:getstat(Sock#ssl_socket.tcp, Stats);
-getstat(Sock, Stats) when is_port(Sock) ->
- inet:getstat(Sock, Stats).
-
-recv(Sock) when ?IS_SSL(Sock) ->
- recv(Sock#ssl_socket.ssl, {ssl, ssl_closed, ssl_error});
-recv(Sock) when is_port(Sock) ->
- recv(Sock, {tcp, tcp_closed, tcp_error}).
-
-recv(S, {DataTag, ClosedTag, ErrorTag}) ->
- receive
- {DataTag, S, Data} -> {data, Data};
- {ClosedTag, S} -> closed;
- {ErrorTag, S, Reason} -> {error, Reason};
- Other -> {other, Other}
- end.
-
-sync_recv(Sock, Length) when ?IS_SSL(Sock) ->
- ssl:recv(Sock#ssl_socket.ssl, Length);
-sync_recv(Sock, Length) ->
- gen_tcp:recv(Sock, Length).
-
-async_recv(Sock, Length, Timeout) when ?IS_SSL(Sock) ->
- Pid = self(),
- Ref = make_ref(),
-
- spawn(fun () -> Pid ! {inet_async, Sock, Ref,
- ssl:recv(Sock#ssl_socket.ssl, Length, Timeout)}
- end),
-
- {ok, Ref};
-async_recv(Sock, Length, infinity) when is_port(Sock) ->
- prim_inet:async_recv(Sock, Length, -1);
-async_recv(Sock, Length, Timeout) when is_port(Sock) ->
- prim_inet:async_recv(Sock, Length, Timeout).
-
-port_command(Sock, Data) when ?IS_SSL(Sock) ->
- case ssl:send(Sock#ssl_socket.ssl, Data) of
- ok -> self() ! {inet_reply, Sock, ok},
- true;
- {error, Reason} -> erlang:error(Reason)
- end;
-port_command(Sock, Data) when is_port(Sock) ->
- erlang:port_command(Sock, Data).
-
-getopts(Sock, Options) when ?IS_SSL(Sock) ->
- ssl:getopts(Sock#ssl_socket.ssl, Options);
-getopts(Sock, Options) when is_port(Sock) ->
- inet:getopts(Sock, Options).
-
-setopts(Sock, Options) when ?IS_SSL(Sock) ->
- ssl:setopts(Sock#ssl_socket.ssl, Options);
-setopts(Sock, Options) when is_port(Sock) ->
- inet:setopts(Sock, Options).
-
-send(Sock, Data) when ?IS_SSL(Sock) -> ssl:send(Sock#ssl_socket.ssl, Data);
-send(Sock, Data) when is_port(Sock) -> gen_tcp:send(Sock, Data).
-
-close(Sock) when ?IS_SSL(Sock) -> ssl:close(Sock#ssl_socket.ssl);
-close(Sock) when is_port(Sock) -> gen_tcp:close(Sock).
-
-fast_close(Sock) when ?IS_SSL(Sock) ->
- %% We cannot simply port_close the underlying tcp socket since the
- %% TLS protocol is quite insistent that a proper closing handshake
- %% should take place (see RFC 5245 s7.2.1). So we call ssl:close
- %% instead, but that can block for a very long time, e.g. when
- %% there is lots of pending output and there is tcp backpressure,
- %% or the ssl_connection process has entered the the
- %% workaround_transport_delivery_problems function during
- %% termination, which, inexplicably, does a gen_tcp:recv(Socket,
- %% 0), which may never return if the client doesn't send a FIN or
- %% that gets swallowed by the network. Since there is no timeout
- %% variant of ssl:close, we construct our own.
- {Pid, MRef} = spawn_monitor(fun () -> ssl:close(Sock#ssl_socket.ssl) end),
- erlang:send_after(?SSL_CLOSE_TIMEOUT, self(), {Pid, ssl_close_timeout}),
- receive
- {Pid, ssl_close_timeout} ->
- erlang:demonitor(MRef, [flush]),
- exit(Pid, kill);
- {'DOWN', MRef, process, Pid, _Reason} ->
- ok
- end,
- catch port_close(Sock#ssl_socket.tcp),
- ok;
-fast_close(Sock) when is_port(Sock) ->
- catch port_close(Sock), ok.
-
-sockname(Sock) when ?IS_SSL(Sock) -> ssl:sockname(Sock#ssl_socket.ssl);
-sockname(Sock) when is_port(Sock) -> inet:sockname(Sock).
-
-peername(Sock) when ?IS_SSL(Sock) -> ssl:peername(Sock#ssl_socket.ssl);
-peername(Sock) when is_port(Sock) -> inet:peername(Sock).
-
-peercert(Sock) when ?IS_SSL(Sock) -> ssl:peercert(Sock#ssl_socket.ssl);
-peercert(Sock) when is_port(Sock) -> nossl.
-
-connection_string(Sock, Direction) ->
- case socket_ends(Sock, Direction) of
- {ok, {FromAddress, FromPort, ToAddress, ToPort}} ->
- {ok, rabbit_misc:format(
- "~s:~p -> ~s:~p",
- [maybe_ntoab(FromAddress), FromPort,
- maybe_ntoab(ToAddress), ToPort])};
- Error ->
- Error
- end.
-
-socket_ends(Sock, Direction) ->
- {From, To} = sock_funs(Direction),
- case {From(Sock), To(Sock)} of
- {{ok, {FromAddress, FromPort}}, {ok, {ToAddress, ToPort}}} ->
- {ok, {rdns(FromAddress), FromPort,
- rdns(ToAddress), ToPort}};
- {{error, _Reason} = Error, _} ->
- Error;
- {_, {error, _Reason} = Error} ->
- Error
- end.
-
-maybe_ntoab(Addr) when is_tuple(Addr) -> rabbit_misc:ntoab(Addr);
-maybe_ntoab(Host) -> Host.
-
-rdns(Addr) ->
- case application:get_env(rabbit, reverse_dns_lookups) of
- {ok, true} -> list_to_binary(rabbit_networking:tcp_host(Addr));
- _ -> Addr
- end.
-
-sock_funs(inbound) -> {fun peername/1, fun sockname/1};
-sock_funs(outbound) -> {fun sockname/1, fun peername/1}.
-
-is_loopback(Sock) when is_port(Sock) ; ?IS_SSL(Sock) ->
- case sockname(Sock) of
- {ok, {Addr, _Port}} -> is_loopback(Addr);
- {error, _} -> false
- end;
-%% We could parse the results of inet:getifaddrs() instead. But that
-%% would be more complex and less maybe Windows-compatible...
-is_loopback({127,_,_,_}) -> true;
-is_loopback({0,0,0,0,0,0,0,1}) -> true;
-is_loopback({0,0,0,0,0,65535,AB,CD}) -> is_loopback(ipv4(AB, CD));
-is_loopback(_) -> false.
-
-ipv4(AB, CD) -> {AB bsr 8, AB band 255, CD bsr 8, CD band 255}.
diff --git a/src/rabbit_networking.erl b/src/rabbit_networking.erl
deleted file mode 100644
index f95f8c5818..0000000000
--- a/src/rabbit_networking.erl
+++ /dev/null
@@ -1,608 +0,0 @@
-%% The contents of this file are subject to the Mozilla Public License
-%% Version 1.1 (the "License"); you may not use this file except in
-%% compliance with the License. You may obtain a copy of the License
-%% at http://www.mozilla.org/MPL/
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and
-%% limitations under the License.
-%%
-%% The Original Code is RabbitMQ.
-%%
-%% The Initial Developer of the Original Code is GoPivotal, Inc.
-%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved.
-%%
-
--module(rabbit_networking).
-
--export([boot/0, start/0, start_tcp_listener/1, start_ssl_listener/2,
- stop_tcp_listener/1, on_node_down/1, active_listeners/0,
- node_listeners/1, register_connection/1, unregister_connection/1,
- connections/0, connection_info_keys/0,
- connection_info/1, connection_info/2,
- connection_info_all/0, connection_info_all/1,
- close_connection/2, force_connection_event_refresh/1, tcp_host/1]).
-
-%%used by TCP-based transports, e.g. STOMP adapter
--export([tcp_listener_addresses/1, tcp_listener_spec/6,
- ensure_ssl/0, fix_ssl_options/1, poodle_check/1, ssl_transform_fun/1]).
-
--export([tcp_listener_started/3, tcp_listener_stopped/3,
- start_client/1, start_ssl_client/2]).
-
-%% Internal
--export([connections_local/0]).
-
--import(rabbit_misc, [pget/2, pget/3, pset/3]).
-
--include("rabbit.hrl").
--include_lib("kernel/include/inet.hrl").
-
--define(FIRST_TEST_BIND_PORT, 10000).
-
-%% POODLE
--define(BAD_SSL_PROTOCOL_VERSIONS, [sslv3]).
-
-%%----------------------------------------------------------------------------
-
--ifdef(use_specs).
-
--export_type([ip_port/0, hostname/0]).
-
--type(hostname() :: inet:hostname()).
--type(ip_port() :: inet:port_number()).
-
--type(family() :: atom()).
--type(listener_config() :: ip_port() |
- {hostname(), ip_port()} |
- {hostname(), ip_port(), family()}).
--type(address() :: {inet:ip_address(), ip_port(), family()}).
--type(name_prefix() :: atom()).
--type(protocol() :: atom()).
--type(label() :: string()).
-
--spec(start/0 :: () -> 'ok').
--spec(start_tcp_listener/1 :: (listener_config()) -> 'ok').
--spec(start_ssl_listener/2 ::
- (listener_config(), rabbit_types:infos()) -> 'ok').
--spec(stop_tcp_listener/1 :: (listener_config()) -> 'ok').
--spec(active_listeners/0 :: () -> [rabbit_types:listener()]).
--spec(node_listeners/1 :: (node()) -> [rabbit_types:listener()]).
--spec(register_connection/1 :: (pid()) -> ok).
--spec(unregister_connection/1 :: (pid()) -> ok).
--spec(connections/0 :: () -> [rabbit_types:connection()]).
--spec(connections_local/0 :: () -> [rabbit_types:connection()]).
--spec(connection_info_keys/0 :: () -> rabbit_types:info_keys()).
--spec(connection_info/1 ::
- (rabbit_types:connection()) -> rabbit_types:infos()).
--spec(connection_info/2 ::
- (rabbit_types:connection(), rabbit_types:info_keys())
- -> rabbit_types:infos()).
--spec(connection_info_all/0 :: () -> [rabbit_types:infos()]).
--spec(connection_info_all/1 ::
- (rabbit_types:info_keys()) -> [rabbit_types:infos()]).
--spec(close_connection/2 :: (pid(), string()) -> 'ok').
--spec(force_connection_event_refresh/1 :: (reference()) -> 'ok').
-
--spec(on_node_down/1 :: (node()) -> 'ok').
--spec(tcp_listener_addresses/1 :: (listener_config()) -> [address()]).
--spec(tcp_listener_spec/6 ::
- (name_prefix(), address(), [gen_tcp:listen_option()], protocol(),
- label(), rabbit_types:mfargs()) -> supervisor:child_spec()).
--spec(ensure_ssl/0 :: () -> rabbit_types:infos()).
--spec(fix_ssl_options/1 :: (rabbit_types:infos()) -> rabbit_types:infos()).
--spec(poodle_check/1 :: (atom()) -> 'ok' | 'danger').
--spec(ssl_transform_fun/1 ::
- (rabbit_types:infos())
- -> fun ((rabbit_net:socket())
- -> rabbit_types:ok_or_error(#ssl_socket{}))).
-
--spec(boot/0 :: () -> 'ok').
--spec(start_client/1 ::
- (port() | #ssl_socket{ssl::{'sslsocket',_,_}}) ->
- atom() | pid() | port() | {atom(),atom()}).
--spec(start_ssl_client/2 ::
- (_,port() | #ssl_socket{ssl::{'sslsocket',_,_}}) ->
- atom() | pid() | port() | {atom(),atom()}).
--spec(tcp_listener_started/3 ::
- (_,
- string() |
- {byte(),byte(),byte(),byte()} |
- {char(),char(),char(),char(),char(),char(),char(),char()},
- _) ->
- 'ok').
--spec(tcp_listener_stopped/3 ::
- (_,
- string() |
- {byte(),byte(),byte(),byte()} |
- {char(),char(),char(),char(),char(),char(),char(),char()},
- _) ->
- 'ok').
-
--endif.
-
-%%----------------------------------------------------------------------------
-
-boot() ->
- ok = record_distribution_listener(),
- ok = start(),
- ok = boot_tcp(),
- ok = boot_ssl().
-
-boot_tcp() ->
- {ok, TcpListeners} = application:get_env(tcp_listeners),
- [ok = start_tcp_listener(Listener) || Listener <- TcpListeners],
- ok.
-
-boot_ssl() ->
- case application:get_env(ssl_listeners) of
- {ok, []} ->
- ok;
- {ok, SslListeners} ->
- SslOpts = ensure_ssl(),
- case poodle_check('AMQP') of
- ok -> [start_ssl_listener(L, SslOpts) || L <- SslListeners];
- danger -> ok
- end,
- ok
- end.
-
-start() -> rabbit_sup:start_supervisor_child(
- rabbit_tcp_client_sup, rabbit_client_sup,
- [{local, rabbit_tcp_client_sup},
- {rabbit_connection_sup,start_link,[]}]).
-
-ensure_ssl() ->
- {ok, SslAppsConfig} = application:get_env(rabbit, ssl_apps),
- ok = app_utils:start_applications(SslAppsConfig),
- {ok, SslOptsConfig} = application:get_env(rabbit, ssl_options),
- fix_ssl_options(SslOptsConfig).
-
-poodle_check(Context) ->
- {ok, Vsn} = application:get_key(ssl, vsn),
- case rabbit_misc:version_compare(Vsn, "5.3", gte) of %% R16B01
- true -> ok;
- false -> case application:get_env(rabbit, ssl_allow_poodle_attack) of
- {ok, true} -> ok;
- _ -> log_poodle_fail(Context),
- danger
- end
- end.
-
-log_poodle_fail(Context) ->
- rabbit_log:error(
- "The installed version of Erlang (~s) contains the bug OTP-10905,~n"
- "which makes it impossible to disable SSLv3. This makes the system~n"
- "vulnerable to the POODLE attack. SSL listeners for ~s have therefore~n"
- "been disabled.~n~n"
- "You are advised to upgrade to a recent Erlang version; R16B01 is the~n"
- "first version in which this bug is fixed, but later is usually~n"
- "better.~n~n"
- "If you cannot upgrade now and want to re-enable SSL listeners, you can~n"
- "set the config item 'ssl_allow_poodle_attack' to 'true' in the~n"
- "'rabbit' section of your configuration file.~n",
- [rabbit_misc:otp_release(), Context]).
-
-fix_ssl_options(Config) ->
- fix_verify_fun(fix_ssl_protocol_versions(Config)).
-
-fix_verify_fun(SslOptsConfig) ->
- %% Starting with ssl 4.0.1 in Erlang R14B, the verify_fun function
- %% takes 3 arguments and returns a tuple.
- {ok, SslAppVer} = application:get_key(ssl, vsn),
- UseNewVerifyFun = rabbit_misc:version_compare(SslAppVer, "4.0.1", gte),
- case rabbit_misc:pget(verify_fun, SslOptsConfig) of
- {Module, Function, InitialUserState} ->
- Fun = make_verify_fun(Module, Function, InitialUserState,
- UseNewVerifyFun),
- rabbit_misc:pset(verify_fun, Fun, SslOptsConfig);
- {Module, Function} ->
- Fun = make_verify_fun(Module, Function, none,
- UseNewVerifyFun),
- rabbit_misc:pset(verify_fun, Fun, SslOptsConfig);
- undefined when UseNewVerifyFun ->
- SslOptsConfig;
- undefined ->
- % unknown_ca errors are silently ignored prior to R14B unless we
- % supply this verify_fun - remove when at least R14B is required
- case proplists:get_value(verify, SslOptsConfig, verify_none) of
- verify_none -> SslOptsConfig;
- verify_peer -> [{verify_fun, fun([]) -> true;
- ([_|_]) -> false
- end}
- | SslOptsConfig]
- end
- end.
-
-make_verify_fun(Module, Function, InitialUserState, UseNewVerifyFun) ->
- try
- %% Preload the module: it is required to use
- %% erlang:function_exported/3.
- Module:module_info()
- catch
- _:Exception ->
- rabbit_log:error("SSL verify_fun: module ~s missing: ~p~n",
- [Module, Exception]),
- throw({error, {invalid_verify_fun, missing_module}})
- end,
- NewForm = erlang:function_exported(Module, Function, 3),
- OldForm = erlang:function_exported(Module, Function, 1),
- case {NewForm, OldForm} of
- {true, _} when UseNewVerifyFun ->
- %% This verify_fun is supported by Erlang R14B+ (ssl
- %% 4.0.1 and later).
- Fun = fun(OtpCert, Event, UserState) ->
- Module:Function(OtpCert, Event, UserState)
- end,
- {Fun, InitialUserState};
- {_, true} ->
- %% This verify_fun is supported by:
- %% o Erlang up-to R13B;
- %% o Erlang R14B+ for undocumented backward
- %% compatibility.
- %%
- %% InitialUserState is ignored in this case.
- fun(ErrorList) ->
- Module:Function(ErrorList)
- end;
- {_, false} when not UseNewVerifyFun ->
- rabbit_log:error("SSL verify_fun: ~s:~s/1 form required "
- "for Erlang R13B~n", [Module, Function]),
- throw({error, {invalid_verify_fun, old_form_required}});
- _ ->
- Arity = case UseNewVerifyFun of true -> 3; _ -> 1 end,
- rabbit_log:error("SSL verify_fun: no ~s:~s/~b exported~n",
- [Module, Function, Arity]),
- throw({error, {invalid_verify_fun, function_not_exported}})
- end.
-
-fix_ssl_protocol_versions(Config) ->
- case application:get_env(rabbit, ssl_allow_poodle_attack) of
- {ok, true} ->
- Config;
- _ ->
- Configured = case pget(versions, Config) of
- undefined -> pget(available, ssl:versions(), []);
- Vs -> Vs
- end,
- pset(versions, Configured -- ?BAD_SSL_PROTOCOL_VERSIONS, Config)
- end.
-
-ssl_timeout() ->
- {ok, Val} = application:get_env(rabbit, ssl_handshake_timeout),
- Val.
-
-ssl_transform_fun(SslOpts) ->
- fun (Sock) ->
- Timeout = ssl_timeout(),
- case catch ssl:ssl_accept(Sock, SslOpts, Timeout) of
- {ok, SslSock} ->
- {ok, #ssl_socket{tcp = Sock, ssl = SslSock}};
- {error, timeout} ->
- {error, {ssl_upgrade_error, timeout}};
- {error, Reason} ->
- %% We have no idea what state the ssl_connection
- %% process is in - it could still be happily
- %% going, it might be stuck, or it could be just
- %% about to fail. There is little that our caller
- %% can do but close the TCP socket, but this could
- %% cause ssl alerts to get dropped (which is bad
- %% form, according to the TLS spec). So we give
- %% the ssl_connection a little bit of time to send
- %% such alerts.
- timer:sleep(Timeout),
- {error, {ssl_upgrade_error, Reason}};
- {'EXIT', Reason} ->
- {error, {ssl_upgrade_failure, Reason}}
- end
- end.
-
-tcp_listener_addresses(Port) when is_integer(Port) ->
- tcp_listener_addresses_auto(Port);
-tcp_listener_addresses({"auto", Port}) ->
- %% Variant to prevent lots of hacking around in bash and batch files
- tcp_listener_addresses_auto(Port);
-tcp_listener_addresses({Host, Port}) ->
- %% auto: determine family IPv4 / IPv6 after converting to IP address
- tcp_listener_addresses({Host, Port, auto});
-tcp_listener_addresses({Host, Port, Family0})
- when is_integer(Port) andalso (Port >= 0) andalso (Port =< 65535) ->
- [{IPAddress, Port, Family} ||
- {IPAddress, Family} <- getaddr(Host, Family0)];
-tcp_listener_addresses({_Host, Port, _Family0}) ->
- rabbit_log:error("invalid port ~p - not 0..65535~n", [Port]),
- throw({error, {invalid_port, Port}}).
-
-tcp_listener_addresses_auto(Port) ->
- lists:append([tcp_listener_addresses(Listener) ||
- Listener <- port_to_listeners(Port)]).
-
-tcp_listener_spec(NamePrefix, {IPAddress, Port, Family}, SocketOpts,
- Protocol, Label, OnConnect) ->
- {rabbit_misc:tcp_name(NamePrefix, IPAddress, Port),
- {tcp_listener_sup, start_link,
- [IPAddress, Port, [Family | SocketOpts],
- {?MODULE, tcp_listener_started, [Protocol]},
- {?MODULE, tcp_listener_stopped, [Protocol]},
- OnConnect, Label]},
- transient, infinity, supervisor, [tcp_listener_sup]}.
-
-start_tcp_listener(Listener) ->
- start_listener(Listener, amqp, "TCP Listener",
- {?MODULE, start_client, []}).
-
-start_ssl_listener(Listener, SslOpts) ->
- start_listener(Listener, 'amqp/ssl', "SSL Listener",
- {?MODULE, start_ssl_client, [SslOpts]}).
-
-start_listener(Listener, Protocol, Label, OnConnect) ->
- [start_listener0(Address, Protocol, Label, OnConnect) ||
- Address <- tcp_listener_addresses(Listener)],
- ok.
-
-start_listener0(Address, Protocol, Label, OnConnect) ->
- Spec = tcp_listener_spec(rabbit_tcp_listener_sup, Address, tcp_opts(),
- Protocol, Label, OnConnect),
- case supervisor:start_child(rabbit_sup, Spec) of
- {ok, _} -> ok;
- {error, {shutdown, _}} -> {IPAddress, Port, _Family} = Address,
- exit({could_not_start_tcp_listener,
- {rabbit_misc:ntoa(IPAddress), Port}})
- end.
-
-stop_tcp_listener(Listener) ->
- [stop_tcp_listener0(Address) ||
- Address <- tcp_listener_addresses(Listener)],
- ok.
-
-stop_tcp_listener0({IPAddress, Port, _Family}) ->
- Name = rabbit_misc:tcp_name(rabbit_tcp_listener_sup, IPAddress, Port),
- ok = supervisor:terminate_child(rabbit_sup, Name),
- ok = supervisor:delete_child(rabbit_sup, Name).
-
-tcp_listener_started(Protocol, IPAddress, Port) ->
- %% We need the ip to distinguish e.g. 0.0.0.0 and 127.0.0.1
- %% We need the host so we can distinguish multiple instances of the above
- %% in a cluster.
- ok = mnesia:dirty_write(
- rabbit_listener,
- #listener{node = node(),
- protocol = Protocol,
- host = tcp_host(IPAddress),
- ip_address = IPAddress,
- port = Port}).
-
-tcp_listener_stopped(Protocol, IPAddress, Port) ->
- ok = mnesia:dirty_delete_object(
- rabbit_listener,
- #listener{node = node(),
- protocol = Protocol,
- host = tcp_host(IPAddress),
- ip_address = IPAddress,
- port = Port}).
-
-record_distribution_listener() ->
- {Name, Host} = rabbit_nodes:parts(node()),
- {port, Port, _Version} = erl_epmd:port_please(Name, Host),
- tcp_listener_started(clustering, {0,0,0,0,0,0,0,0}, Port).
-
-active_listeners() ->
- rabbit_misc:dirty_read_all(rabbit_listener).
-
-node_listeners(Node) ->
- mnesia:dirty_read(rabbit_listener, Node).
-
-on_node_down(Node) ->
- case lists:member(Node, nodes()) of
- false -> ok = mnesia:dirty_delete(rabbit_listener, Node);
- true -> rabbit_log:info(
- "Keep ~s listeners: the node is already back~n", [Node])
- end.
-
-start_client(Sock, SockTransform) ->
- {ok, _Child, Reader} = supervisor:start_child(rabbit_tcp_client_sup, []),
- ok = rabbit_net:controlling_process(Sock, Reader),
- Reader ! {go, Sock, SockTransform},
-
- %% In the event that somebody floods us with connections, the
- %% reader processes can spew log events at error_logger faster
- %% than it can keep up, causing its mailbox to grow unbounded
- %% until we eat all the memory available and crash. So here is a
- %% meaningless synchronous call to the underlying gen_event
- %% mechanism. When it returns the mailbox is drained, and we
- %% return to our caller to accept more connetions.
- gen_event:which_handlers(error_logger),
-
- Reader.
-
-start_client(Sock) ->
- start_client(Sock, fun (S) -> {ok, S} end).
-
-start_ssl_client(SslOpts, Sock) ->
- start_client(Sock, ssl_transform_fun(SslOpts)).
-
-register_connection(Pid) -> pg_local:join(rabbit_connections, Pid).
-
-unregister_connection(Pid) -> pg_local:leave(rabbit_connections, Pid).
-
-connections() ->
- rabbit_misc:append_rpc_all_nodes(rabbit_mnesia:cluster_nodes(running),
- rabbit_networking, connections_local, []).
-
-connections_local() -> pg_local:get_members(rabbit_connections).
-
-connection_info_keys() -> rabbit_reader:info_keys().
-
-connection_info(Pid) -> rabbit_reader:info(Pid).
-connection_info(Pid, Items) -> rabbit_reader:info(Pid, Items).
-
-connection_info_all() -> cmap(fun (Q) -> connection_info(Q) end).
-connection_info_all(Items) -> cmap(fun (Q) -> connection_info(Q, Items) end).
-
-close_connection(Pid, Explanation) ->
- rabbit_log:info("Closing connection ~p because ~p~n", [Pid, Explanation]),
- case lists:member(Pid, connections()) of
- true -> rabbit_reader:shutdown(Pid, Explanation);
- false -> throw({error, {not_a_connection_pid, Pid}})
- end.
-
-force_connection_event_refresh(Ref) ->
- [rabbit_reader:force_event_refresh(C, Ref) || C <- connections()],
- ok.
-
-%%--------------------------------------------------------------------
-
-tcp_host({0,0,0,0}) ->
- hostname();
-
-tcp_host({0,0,0,0,0,0,0,0}) ->
- hostname();
-
-tcp_host(IPAddress) ->
- case inet:gethostbyaddr(IPAddress) of
- {ok, #hostent{h_name = Name}} -> Name;
- {error, _Reason} -> rabbit_misc:ntoa(IPAddress)
- end.
-
-hostname() ->
- {ok, Hostname} = inet:gethostname(),
- case inet:gethostbyname(Hostname) of
- {ok, #hostent{h_name = Name}} -> Name;
- {error, _Reason} -> Hostname
- end.
-
-cmap(F) -> rabbit_misc:filter_exit_map(F, connections()).
-
-tcp_opts() ->
- {ok, ConfigOpts} = application:get_env(rabbit, tcp_listen_options),
- merge_essential_tcp_listen_options(ConfigOpts).
-
--define(ESSENTIAL_LISTEN_OPTIONS,
- [binary,
- {active, false},
- {packet, raw},
- {reuseaddr, true},
- {nodelay, true}]).
-
-merge_essential_tcp_listen_options(Opts) ->
- lists:foldl(fun ({K, _} = Opt, Acc) ->
- lists:keystore(K, 1, Acc, Opt);
- (Opt, Acc) ->
- [Opt | Acc]
- end , Opts, ?ESSENTIAL_LISTEN_OPTIONS).
-
-%% inet_parse:address takes care of ip string, like "0.0.0.0"
-%% inet:getaddr returns immediately for ip tuple {0,0,0,0},
-%% and runs 'inet_gethost' port process for dns lookups.
-%% On Windows inet:getaddr runs dns resolver for ip string, which may fail.
-getaddr(Host, Family) ->
- case inet_parse:address(Host) of
- {ok, IPAddress} -> [{IPAddress, resolve_family(IPAddress, Family)}];
- {error, _} -> gethostaddr(Host, Family)
- end.
-
-gethostaddr(Host, auto) ->
- Lookups = [{Family, inet:getaddr(Host, Family)} || Family <- [inet, inet6]],
- case [{IP, Family} || {Family, {ok, IP}} <- Lookups] of
- [] -> host_lookup_error(Host, Lookups);
- IPs -> IPs
- end;
-
-gethostaddr(Host, Family) ->
- case inet:getaddr(Host, Family) of
- {ok, IPAddress} -> [{IPAddress, Family}];
- {error, Reason} -> host_lookup_error(Host, Reason)
- end.
-
-host_lookup_error(Host, Reason) ->
- rabbit_log:error("invalid host ~p - ~p~n", [Host, Reason]),
- throw({error, {invalid_host, Host, Reason}}).
-
-resolve_family({_,_,_,_}, auto) -> inet;
-resolve_family({_,_,_,_,_,_,_,_}, auto) -> inet6;
-resolve_family(IP, auto) -> throw({error, {strange_family, IP}});
-resolve_family(_, F) -> F.
-
-%%--------------------------------------------------------------------
-
-%% There are three kinds of machine (for our purposes).
-%%
-%% * Those which treat IPv4 addresses as a special kind of IPv6 address
-%% ("Single stack")
-%% - Linux by default, Windows Vista and later
-%% - We also treat any (hypothetical?) IPv6-only machine the same way
-%% * Those which consider IPv6 and IPv4 to be completely separate things
-%% ("Dual stack")
-%% - OpenBSD, Windows XP / 2003, Linux if so configured
-%% * Those which do not support IPv6.
-%% - Ancient/weird OSes, Linux if so configured
-%%
-%% How to reconfigure Linux to test this:
-%% Single stack (default):
-%% echo 0 > /proc/sys/net/ipv6/bindv6only
-%% Dual stack:
-%% echo 1 > /proc/sys/net/ipv6/bindv6only
-%% IPv4 only:
-%% add ipv6.disable=1 to GRUB_CMDLINE_LINUX_DEFAULT in /etc/default/grub then
-%% sudo update-grub && sudo reboot
-%%
-%% This matters in (and only in) the case where the sysadmin (or the
-%% app descriptor) has only supplied a port and we wish to bind to
-%% "all addresses". This means different things depending on whether
-%% we're single or dual stack. On single stack binding to "::"
-%% implicitly includes all IPv4 addresses, and subsequently attempting
-%% to bind to "0.0.0.0" will fail. On dual stack, binding to "::" will
-%% only bind to IPv6 addresses, and we need another listener bound to
-%% "0.0.0.0" for IPv4. Finally, on IPv4-only systems we of course only
-%% want to bind to "0.0.0.0".
-%%
-%% Unfortunately it seems there is no way to detect single vs dual stack
-%% apart from attempting to bind to the port.
-port_to_listeners(Port) ->
- IPv4 = {"0.0.0.0", Port, inet},
- IPv6 = {"::", Port, inet6},
- case ipv6_status(?FIRST_TEST_BIND_PORT) of
- single_stack -> [IPv6];
- ipv6_only -> [IPv6];
- dual_stack -> [IPv6, IPv4];
- ipv4_only -> [IPv4]
- end.
-
-ipv6_status(TestPort) ->
- IPv4 = [inet, {ip, {0,0,0,0}}],
- IPv6 = [inet6, {ip, {0,0,0,0,0,0,0,0}}],
- case gen_tcp:listen(TestPort, IPv6) of
- {ok, LSock6} ->
- case gen_tcp:listen(TestPort, IPv4) of
- {ok, LSock4} ->
- %% Dual stack
- gen_tcp:close(LSock6),
- gen_tcp:close(LSock4),
- dual_stack;
- %% Checking the error here would only let us
- %% distinguish single stack IPv6 / IPv4 vs IPv6 only,
- %% which we figure out below anyway.
- {error, _} ->
- gen_tcp:close(LSock6),
- case gen_tcp:listen(TestPort, IPv4) of
- %% Single stack
- {ok, LSock4} -> gen_tcp:close(LSock4),
- single_stack;
- %% IPv6-only machine. Welcome to the future.
- {error, eafnosupport} -> ipv6_only; %% Linux
- {error, eprotonosupport}-> ipv6_only; %% FreeBSD
- %% Dual stack machine with something already
- %% on IPv4.
- {error, _} -> ipv6_status(TestPort + 1)
- end
- end;
- %% IPv4-only machine. Welcome to the 90s.
- {error, eafnosupport} -> %% Linux
- ipv4_only;
- {error, eprotonosupport} -> %% FreeBSD
- ipv4_only;
- %% Port in use
- {error, _} ->
- ipv6_status(TestPort + 1)
- end.
diff --git a/src/rabbit_nodes.erl b/src/rabbit_nodes.erl
deleted file mode 100644
index 57d971715b..0000000000
--- a/src/rabbit_nodes.erl
+++ /dev/null
@@ -1,221 +0,0 @@
-%% The contents of this file are subject to the Mozilla Public License
-%% Version 1.1 (the "License"); you may not use this file except in
-%% compliance with the License. You may obtain a copy of the License
-%% at http://www.mozilla.org/MPL/
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and
-%% limitations under the License.
-%%
-%% The Original Code is RabbitMQ.
-%%
-%% The Initial Developer of the Original Code is GoPivotal, Inc.
-%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved.
-%%
-
--module(rabbit_nodes).
-
--export([names/1, diagnostics/1, make/1, parts/1, cookie_hash/0,
- is_running/2, is_process_running/2,
- cluster_name/0, set_cluster_name/1, ensure_epmd/0,
- all_running/0]).
-
--include_lib("kernel/include/inet.hrl").
-
--define(EPMD_TIMEOUT, 30000).
--define(TCP_DIAGNOSTIC_TIMEOUT, 5000).
-
-%%----------------------------------------------------------------------------
-%% Specs
-%%----------------------------------------------------------------------------
-
--ifdef(use_specs).
-
--spec(names/1 :: (string()) -> rabbit_types:ok_or_error2(
- [{string(), integer()}], term())).
--spec(diagnostics/1 :: ([node()]) -> string()).
--spec(make/1 :: ({string(), string()} | string()) -> node()).
--spec(parts/1 :: (node() | string()) -> {string(), string()}).
--spec(cookie_hash/0 :: () -> string()).
--spec(is_running/2 :: (node(), atom()) -> boolean()).
--spec(is_process_running/2 :: (node(), atom()) -> boolean()).
--spec(cluster_name/0 :: () -> binary()).
--spec(set_cluster_name/1 :: (binary()) -> 'ok').
--spec(ensure_epmd/0 :: () -> 'ok').
--spec(all_running/0 :: () -> [node()]).
-
--endif.
-
-%%----------------------------------------------------------------------------
-
-names(Hostname) ->
- Self = self(),
- Ref = make_ref(),
- {Pid, MRef} = spawn_monitor(
- fun () -> Self ! {Ref, net_adm:names(Hostname)} end),
- timer:exit_after(?EPMD_TIMEOUT, Pid, timeout),
- receive
- {Ref, Names} -> erlang:demonitor(MRef, [flush]),
- Names;
- {'DOWN', MRef, process, Pid, Reason} -> {error, Reason}
- end.
-
-diagnostics(Nodes) ->
- NodeDiags = [{"~nDIAGNOSTICS~n===========~n~n"
- "attempted to contact: ~p~n", [Nodes]}] ++
- [diagnostics_node(Node) || Node <- Nodes] ++
- current_node_details(),
- rabbit_misc:format_many(lists:flatten(NodeDiags)).
-
-current_node_details() ->
- [{"~ncurrent node details:~n- node name: ~w", [node()]},
- case init:get_argument(home) of
- {ok, [[Home]]} -> {"- home dir: ~s", [Home]};
- Other -> {"- no home dir: ~p", [Other]}
- end,
- {"- cookie hash: ~s", [cookie_hash()]}].
-
-diagnostics_node(Node) ->
- {Name, Host} = parts(Node),
- [{"~s:", [Node]} |
- case names(Host) of
- {error, Reason} ->
- [{" * unable to connect to epmd (port ~s) on ~s: ~s~n",
- [epmd_port(), Host, rabbit_misc:format_inet_error(Reason)]}];
- {ok, NamePorts} ->
- [{" * connected to epmd (port ~s) on ~s",
- [epmd_port(), Host]}] ++
- case net_adm:ping(Node) of
- pong -> dist_working_diagnostics(Node);
- pang -> dist_broken_diagnostics(Name, Host, NamePorts)
- end
- end].
-
-epmd_port() ->
- case init:get_argument(epmd_port) of
- {ok, [[Port | _] | _]} when is_list(Port) -> Port;
- error -> "4369"
- end.
-
-dist_working_diagnostics(Node) ->
- case rabbit:is_running(Node) of
- true -> [{" * node ~s up, 'rabbit' application running", [Node]}];
- false -> [{" * node ~s up, 'rabbit' application not running~n"
- " * running applications on ~s: ~p~n"
- " * suggestion: start_app on ~s",
- [Node, Node, remote_apps(Node), Node]}]
- end.
-
-remote_apps(Node) ->
- %% We want a timeout here because really, we don't trust the node,
- %% the last thing we want to do is hang.
- case rpc:call(Node, application, which_applications, [5000]) of
- {badrpc, _} = E -> E;
- Apps -> [App || {App, _, _} <- Apps]
- end.
-
-dist_broken_diagnostics(Name, Host, NamePorts) ->
- case [{N, P} || {N, P} <- NamePorts, N =:= Name] of
- [] ->
- {SelfName, SelfHost} = parts(node()),
- Others = [list_to_atom(N) || {N, _} <- NamePorts,
- N =/= case SelfHost of
- Host -> SelfName;
- _ -> never_matches
- end],
- OthersDiag = case Others of
- [] -> [{" no other nodes on ~s",
- [Host]}];
- _ -> [{" other nodes on ~s: ~p",
- [Host, Others]}]
- end,
- [{" * epmd reports: node '~s' not running at all", [Name]},
- OthersDiag, {" * suggestion: start the node", []}];
- [{Name, Port}] ->
- [{" * epmd reports node '~s' running on port ~b", [Name, Port]} |
- case diagnose_connect(Host, Port) of
- ok ->
- [{" * TCP connection succeeded but Erlang distribution "
- "failed~n"
- " * suggestion: hostname mismatch?~n"
- " * suggestion: is the cookie set correctly?~n"
- " * suggestion: is the Erlang distribution using TLS?", []}];
- {error, Reason} ->
- [{" * can't establish TCP connection, reason: ~s~n"
- " * suggestion: blocked by firewall?",
- [rabbit_misc:format_inet_error(Reason)]}]
- end]
- end.
-
-diagnose_connect(Host, Port) ->
- case inet:gethostbyname(Host) of
- {ok, #hostent{h_addrtype = Family}} ->
- case gen_tcp:connect(Host, Port, [Family],
- ?TCP_DIAGNOSTIC_TIMEOUT) of
- {ok, Socket} -> gen_tcp:close(Socket),
- ok;
- {error, _} = E -> E
- end;
- {error, _} = E ->
- E
- end.
-
-make({Prefix, Suffix}) -> list_to_atom(lists:append([Prefix, "@", Suffix]));
-make(NodeStr) -> make(parts(NodeStr)).
-
-parts(Node) when is_atom(Node) ->
- parts(atom_to_list(Node));
-parts(NodeStr) ->
- case lists:splitwith(fun (E) -> E =/= $@ end, NodeStr) of
- {Prefix, []} -> {_, Suffix} = parts(node()),
- {Prefix, Suffix};
- {Prefix, Suffix} -> {Prefix, tl(Suffix)}
- end.
-
-cookie_hash() ->
- base64:encode_to_string(erlang:md5(atom_to_list(erlang:get_cookie()))).
-
-is_running(Node, Application) ->
- case rpc:call(Node, rabbit_misc, which_applications, []) of
- {badrpc, _} -> false;
- Apps -> proplists:is_defined(Application, Apps)
- end.
-
-is_process_running(Node, Process) ->
- case rpc:call(Node, erlang, whereis, [Process]) of
- {badrpc, _} -> false;
- undefined -> false;
- P when is_pid(P) -> true
- end.
-
-cluster_name() ->
- rabbit_runtime_parameters:value_global(
- cluster_name, cluster_name_default()).
-
-cluster_name_default() ->
- {ID, _} = rabbit_nodes:parts(node()),
- {ok, Host} = inet:gethostname(),
- {ok, #hostent{h_name = FQDN}} = inet:gethostbyname(Host),
- list_to_binary(atom_to_list(rabbit_nodes:make({ID, FQDN}))).
-
-set_cluster_name(Name) ->
- rabbit_runtime_parameters:set_global(cluster_name, Name).
-
-ensure_epmd() ->
- {ok, Prog} = init:get_argument(progname),
- ID = random:uniform(1000000000),
- Port = open_port(
- {spawn_executable, os:find_executable(Prog)},
- [{args, ["-sname", rabbit_misc:format("epmd-starter-~b", [ID]),
- "-noshell", "-eval", "halt()."]},
- exit_status, stderr_to_stdout, use_stdio]),
- port_shutdown_loop(Port).
-
-port_shutdown_loop(Port) ->
- receive
- {Port, {exit_status, _Rc}} -> ok;
- {Port, _} -> port_shutdown_loop(Port)
- end.
-
-all_running() -> rabbit_mnesia:cluster_nodes(running).
diff --git a/src/rabbit_policy_validator.erl b/src/rabbit_policy_validator.erl
deleted file mode 100644
index 7ebea83516..0000000000
--- a/src/rabbit_policy_validator.erl
+++ /dev/null
@@ -1,39 +0,0 @@
-%% The contents of this file are subject to the Mozilla Public License
-%% Version 1.1 (the "License"); you may not use this file except in
-%% compliance with the License. You may obtain a copy of the License
-%% at http://www.mozilla.org/MPL/
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and
-%% limitations under the License.
-%%
-%% The Original Code is RabbitMQ.
-%%
-%% The Initial Developer of the Original Code is GoPivotal, Inc.
-%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved.
-%%
-
--module(rabbit_policy_validator).
-
--ifdef(use_specs).
-
--export_type([validate_results/0]).
-
--type(validate_results() ::
- 'ok' | {error, string(), [term()]} | [validate_results()]).
-
--callback validate_policy([{binary(), term()}]) -> validate_results().
-
--else.
-
--export([behaviour_info/1]).
-
-behaviour_info(callbacks) ->
- [
- {validate_policy, 1}
- ];
-behaviour_info(_Other) ->
- undefined.
-
--endif.
diff --git a/src/rabbit_queue_collector.erl b/src/rabbit_queue_collector.erl
deleted file mode 100644
index d4e1c71da2..0000000000
--- a/src/rabbit_queue_collector.erl
+++ /dev/null
@@ -1,95 +0,0 @@
-%% The contents of this file are subject to the Mozilla Public License
-%% Version 1.1 (the "License"); you may not use this file except in
-%% compliance with the License. You may obtain a copy of the License
-%% at http://www.mozilla.org/MPL/
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and
-%% limitations under the License.
-%%
-%% The Original Code is RabbitMQ.
-%%
-%% The Initial Developer of the Original Code is GoPivotal, Inc.
-%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved.
-%%
-
--module(rabbit_queue_collector).
-
-%% Queue collector keeps track of exclusive queues and cleans them
-%% up e.g. when their connection is closed.
-
--behaviour(gen_server).
-
--export([start_link/1, register/2, delete_all/1]).
-
--export([init/1, handle_call/3, handle_cast/2, handle_info/2,
- terminate/2, code_change/3]).
-
--record(state, {monitors, delete_from}).
-
--include("rabbit.hrl").
-
-%%----------------------------------------------------------------------------
-
--ifdef(use_specs).
-
--spec(start_link/1 :: (rabbit_types:proc_name()) ->
- rabbit_types:ok_pid_or_error()).
--spec(register/2 :: (pid(), pid()) -> 'ok').
--spec(delete_all/1 :: (pid()) -> 'ok').
-
--endif.
-
-%%----------------------------------------------------------------------------
-
-start_link(ProcName) ->
- gen_server:start_link(?MODULE, [ProcName], []).
-
-register(CollectorPid, Q) ->
- gen_server:call(CollectorPid, {register, Q}, infinity).
-
-delete_all(CollectorPid) ->
- gen_server:call(CollectorPid, delete_all, infinity).
-
-%%----------------------------------------------------------------------------
-
-init([ProcName]) ->
- ?store_proc_name(ProcName),
- {ok, #state{monitors = pmon:new(), delete_from = undefined}}.
-
-%%--------------------------------------------------------------------------
-
-handle_call({register, QPid}, _From,
- State = #state{monitors = QMons, delete_from = Deleting}) ->
- case Deleting of
- undefined -> ok;
- _ -> ok = rabbit_amqqueue:delete_immediately([QPid])
- end,
- {reply, ok, State#state{monitors = pmon:monitor(QPid, QMons)}};
-
-handle_call(delete_all, From, State = #state{monitors = QMons,
- delete_from = undefined}) ->
- case pmon:monitored(QMons) of
- [] -> {reply, ok, State#state{delete_from = From}};
- QPids -> ok = rabbit_amqqueue:delete_immediately(QPids),
- {noreply, State#state{delete_from = From}}
- end.
-
-handle_cast(Msg, State) ->
- {stop, {unhandled_cast, Msg}, State}.
-
-handle_info({'DOWN', _MRef, process, DownPid, _Reason},
- State = #state{monitors = QMons, delete_from = Deleting}) ->
- QMons1 = pmon:erase(DownPid, QMons),
- case Deleting =/= undefined andalso pmon:is_empty(QMons1) of
- true -> gen_server:reply(Deleting, ok);
- false -> ok
- end,
- {noreply, State#state{monitors = QMons1}}.
-
-terminate(_Reason, _State) ->
- ok.
-
-code_change(_OldVsn, State, _Extra) ->
- {ok, State}.
diff --git a/src/rabbit_queue_decorator.erl b/src/rabbit_queue_decorator.erl
deleted file mode 100644
index 0c6f0820c7..0000000000
--- a/src/rabbit_queue_decorator.erl
+++ /dev/null
@@ -1,80 +0,0 @@
-%% The contents of this file are subject to the Mozilla Public License
-%% Version 1.1 (the "License"); you may not use this file except in
-%% compliance with the License. You may obtain a copy of the License
-%% at http://www.mozilla.org/MPL/
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and
-%% limitations under the License.
-%%
-%% The Original Code is RabbitMQ.
-%%
-%% The Initial Developer of the Original Code is GoPivotal, Inc.
-%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved.
-%%
-
--module(rabbit_queue_decorator).
-
--include("rabbit.hrl").
-
--export([select/1, set/1, register/2, unregister/1]).
-
-%%----------------------------------------------------------------------------
-
--ifdef(use_specs).
-
--callback startup(rabbit_types:amqqueue()) -> 'ok'.
-
--callback shutdown(rabbit_types:amqqueue()) -> 'ok'.
-
--callback policy_changed(rabbit_types:amqqueue(), rabbit_types:amqqueue()) ->
- 'ok'.
-
--callback active_for(rabbit_types:amqqueue()) -> boolean().
-
-%% called with Queue, MaxActivePriority, IsEmpty
--callback consumer_state_changed(
- rabbit_types:amqqueue(), integer(), boolean()) -> 'ok'.
-
--else.
-
--export([behaviour_info/1]).
-
-behaviour_info(callbacks) ->
- [{startup, 1}, {shutdown, 1}, {policy_changed, 2},
- {active_for, 1}, {consumer_state_changed, 3}];
-behaviour_info(_Other) ->
- undefined.
-
--endif.
-
-%%----------------------------------------------------------------------------
-
-select(Modules) ->
- [M || M <- Modules, code:which(M) =/= non_existing].
-
-set(Q) -> Q#amqqueue{decorators = [D || D <- list(), D:active_for(Q)]}.
-
-list() -> [M || {_, M} <- rabbit_registry:lookup_all(queue_decorator)].
-
-register(TypeName, ModuleName) ->
- rabbit_registry:register(queue_decorator, TypeName, ModuleName),
- [maybe_recover(Q) || Q <- rabbit_amqqueue:list()],
- ok.
-
-unregister(TypeName) ->
- rabbit_registry:unregister(queue_decorator, TypeName),
- [maybe_recover(Q) || Q <- rabbit_amqqueue:list()],
- ok.
-
-maybe_recover(Q = #amqqueue{name = Name,
- decorators = Decs}) ->
- #amqqueue{decorators = Decs1} = set(Q),
- Old = lists:sort(select(Decs)),
- New = lists:sort(select(Decs1)),
- case New of
- Old -> ok;
- _ -> [M:startup(Q) || M <- New -- Old],
- rabbit_amqqueue:update_decorators(Name)
- end.
diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl
deleted file mode 100644
index f66a80d811..0000000000
--- a/src/rabbit_reader.erl
+++ /dev/null
@@ -1,1412 +0,0 @@
-%% The contents of this file are subject to the Mozilla Public License
-%% Version 1.1 (the "License"); you may not use this file except in
-%% compliance with the License. You may obtain a copy of the License
-%% at http://www.mozilla.org/MPL/
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and
-%% limitations under the License.
-%%
-%% The Original Code is RabbitMQ.
-%%
-%% The Initial Developer of the Original Code is GoPivotal, Inc.
-%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved.
-%%
-
--module(rabbit_reader).
-
-%% This is an AMQP 0-9-1 connection implementation. If AMQP 1.0 plugin is enabled,
-%% this module passes control of incoming AMQP 1.0 connections to it.
-%%
-%% Every connection (as in, a process using this module)
-%% is a controlling process for a server socket.
-%%
-%% Connections have a number of responsibilities:
-%%
-%% * Performing protocol handshake
-%% * Parsing incoming data and dispatching protocol methods
-%% * Authenticating clients (with the help of authentication backends)
-%% * Enforcing TCP backpressure (throttling clients)
-%% * Enforcing connection limits, e.g. channel_max
-%% * Channel management
-%% * Setting up heartbeater and alarm notifications
-%% * Emitting connection and network activity metric events
-%% * Gracefully handling client disconnects, channel termination, etc
-%%
-%% and a few more.
-%%
-%% Every connection has
-%%
-%% * a queue collector which is responsible for keeping
-%% track of exclusive queues on the connection and their cleanup.
-%% * a heartbeater that's responsible for sending heartbeat frames to clients,
-%% keeping track of the incoming ones and notifying connection about
-%% heartbeat timeouts
-%% * Stats timer, a timer that is used to periodically emit metric events
-%%
-%% Some dependencies are started under a separate supervisor to avoid deadlocks
-%% during system shutdown. See rabbit_channel_sup:start_link/0 for details.
-%%
-%% Reader processes are special processes (in the OTP sense).
-
--include("rabbit_framing.hrl").
--include("rabbit.hrl").
-
--export([start_link/1, info_keys/0, info/1, info/2, force_event_refresh/2,
- shutdown/2]).
-
--export([system_continue/3, system_terminate/4, system_code_change/4]).
-
--export([init/2, mainloop/4, recvloop/4]).
-
--export([conserve_resources/3, server_properties/1]).
-
--define(NORMAL_TIMEOUT, 3).
--define(CLOSING_TIMEOUT, 30).
--define(CHANNEL_TERMINATION_TIMEOUT, 3).
-%% we wait for this many seconds before closing TCP connection
-%% with a client that failed to log in. Provides some relief
-%% from connection storms and DoS.
--define(SILENT_CLOSE_DELAY, 3).
--define(CHANNEL_MIN, 1).
-
-%%--------------------------------------------------------------------------
-
--record(v1, {
- %% parent process
- parent,
- %% socket
- sock,
- %% connection state, see connection record
- connection,
- callback,
- recv_len,
- pending_recv,
- %% pre_init | securing | running | blocking | blocked | closing | closed | {become, F}
- connection_state,
- %% see comment in rabbit_connection_sup:start_link/0
- helper_sup,
- %% takes care of cleaning up exclusive queues,
- %% see rabbit_queue_collector
- queue_collector,
- %% sends and receives heartbeat frames,
- %% see rabbit_heartbeat
- heartbeater,
- %% timer used to emit statistics
- stats_timer,
- %% channel supervisor
- channel_sup_sup_pid,
- %% how many channels this connection has
- channel_count,
- %% throttling state, for both
- %% credit- and resource-driven flow control
- throttle}).
-
--record(connection, {
- %% e.g. <<"127.0.0.1:55054 -> 127.0.0.1:5672">>
- name,
- %% server host
- host,
- %% client host
- peer_host,
- %% server port
- port,
- %% client port
- peer_port,
- %% protocol framing implementation module,
- %% e.g. rabbit_framing_amqp_0_9_1
- protocol,
- user,
- %% heartbeat timeout value used, 0 means
- %% heartbeats are disabled
- timeout_sec,
- %% maximum allowed frame size,
- %% see frame_max in the AMQP 0-9-1 spec
- frame_max,
- %% greatest channel number allowed,
- %% see channel_max in the AMQP 0-9-1 spec
- channel_max,
- vhost,
- %% client name, version, platform, etc
- client_properties,
- %% what lists protocol extensions
- %% does this client support?
- capabilities,
- %% authentication mechanism used
- %% as a pair of {Name, Module}
- auth_mechanism,
- %% authentication mechanism state,
- %% initialised by rabbit_auth_mechanism:init/1
- %% implementations
- auth_state,
- %% time of connection
- connected_at}).
-
--record(throttle, {
- %% list of active alarms
- alarmed_by,
- %% flow | resource
- last_blocked_by,
- %% never | timestamp()
- last_blocked_at
-}).
-
--define(STATISTICS_KEYS, [pid, recv_oct, recv_cnt, send_oct, send_cnt,
- send_pend, state, channels]).
-
--define(CREATION_EVENT_KEYS,
- [pid, name, port, peer_port, host,
- peer_host, ssl, peer_cert_subject, peer_cert_issuer,
- peer_cert_validity, auth_mechanism, ssl_protocol,
- ssl_key_exchange, ssl_cipher, ssl_hash, protocol, user, vhost,
- timeout, frame_max, channel_max, client_properties, connected_at]).
-
--define(INFO_KEYS, ?CREATION_EVENT_KEYS ++ ?STATISTICS_KEYS -- [pid]).
-
--define(AUTH_NOTIFICATION_INFO_KEYS,
- [host, vhost, name, peer_host, peer_port, protocol, auth_mechanism,
- ssl, ssl_protocol, ssl_cipher, peer_cert_issuer, peer_cert_subject,
- peer_cert_validity]).
-
--define(IS_RUNNING(State),
- (State#v1.connection_state =:= running orelse
- State#v1.connection_state =:= blocking orelse
- State#v1.connection_state =:= blocked)).
-
--define(IS_STOPPING(State),
- (State#v1.connection_state =:= closing orelse
- State#v1.connection_state =:= closed)).
-
-%%--------------------------------------------------------------------------
-
--ifdef(use_specs).
-
--spec(start_link/1 :: (pid()) -> rabbit_types:ok(pid())).
--spec(info_keys/0 :: () -> rabbit_types:info_keys()).
--spec(info/1 :: (pid()) -> rabbit_types:infos()).
--spec(info/2 :: (pid(), rabbit_types:info_keys()) -> rabbit_types:infos()).
--spec(force_event_refresh/2 :: (pid(), reference()) -> 'ok').
--spec(shutdown/2 :: (pid(), string()) -> 'ok').
--spec(conserve_resources/3 :: (pid(), atom(), boolean()) -> 'ok').
--spec(server_properties/1 :: (rabbit_types:protocol()) ->
- rabbit_framing:amqp_table()).
-
-%% These specs only exists to add no_return() to keep dialyzer happy
--spec(init/2 :: (pid(), pid()) -> no_return()).
--spec(start_connection/5 ::
- (pid(), pid(), any(), rabbit_net:socket(),
- fun ((rabbit_net:socket()) ->
- rabbit_types:ok_or_error2(
- rabbit_net:socket(), any()))) -> no_return()).
-
--spec(mainloop/4 :: (_,[binary()], non_neg_integer(), #v1{}) -> any()).
--spec(system_code_change/4 :: (_,_,_,_) -> {'ok',_}).
--spec(system_continue/3 :: (_,_,{[binary()], non_neg_integer(), #v1{}}) ->
- any()).
--spec(system_terminate/4 :: (_,_,_,_) -> none()).
-
--endif.
-
-%%--------------------------------------------------------------------------
-
-start_link(HelperSup) ->
- {ok, proc_lib:spawn_link(?MODULE, init, [self(), HelperSup])}.
-
-shutdown(Pid, Explanation) ->
- gen_server:call(Pid, {shutdown, Explanation}, infinity).
-
-init(Parent, HelperSup) ->
- Deb = sys:debug_options([]),
- receive
- {go, Sock, SockTransform} ->
- start_connection(Parent, HelperSup, Deb, Sock, SockTransform)
- end.
-
-system_continue(Parent, Deb, {Buf, BufLen, State}) ->
- mainloop(Deb, Buf, BufLen, State#v1{parent = Parent}).
-
-system_terminate(Reason, _Parent, _Deb, _State) ->
- exit(Reason).
-
-system_code_change(Misc, _Module, _OldVsn, _Extra) ->
- {ok, Misc}.
-
-info_keys() -> ?INFO_KEYS.
-
-info(Pid) ->
- gen_server:call(Pid, info, infinity).
-
-info(Pid, Items) ->
- case gen_server:call(Pid, {info, Items}, infinity) of
- {ok, Res} -> Res;
- {error, Error} -> throw(Error)
- end.
-
-force_event_refresh(Pid, Ref) ->
- gen_server:cast(Pid, {force_event_refresh, Ref}).
-
-conserve_resources(Pid, Source, Conserve) ->
- Pid ! {conserve_resources, Source, Conserve},
- ok.
-
-server_properties(Protocol) ->
- {ok, Product} = application:get_key(rabbit, id),
- {ok, Version} = application:get_key(rabbit, vsn),
-
- %% Get any configuration-specified server properties
- {ok, RawConfigServerProps} = application:get_env(rabbit,
- server_properties),
-
- %% Normalize the simplifed (2-tuple) and unsimplified (3-tuple) forms
- %% from the config and merge them with the generated built-in properties
- NormalizedConfigServerProps =
- [{<<"capabilities">>, table, server_capabilities(Protocol)} |
- [case X of
- {KeyAtom, Value} -> {list_to_binary(atom_to_list(KeyAtom)),
- longstr,
- maybe_list_to_binary(Value)};
- {BinKey, Type, Value} -> {BinKey, Type, Value}
- end || X <- RawConfigServerProps ++
- [{product, Product},
- {version, Version},
- {cluster_name, rabbit_nodes:cluster_name()},
- {platform, "Erlang/OTP"},
- {copyright, ?COPYRIGHT_MESSAGE},
- {information, ?INFORMATION_MESSAGE}]]],
-
- %% Filter duplicated properties in favour of config file provided values
- lists:usort(fun ({K1,_,_}, {K2,_,_}) -> K1 =< K2 end,
- NormalizedConfigServerProps).
-
-maybe_list_to_binary(V) when is_binary(V) -> V;
-maybe_list_to_binary(V) when is_list(V) -> list_to_binary(V).
-
-server_capabilities(rabbit_framing_amqp_0_9_1) ->
- [{<<"publisher_confirms">>, bool, true},
- {<<"exchange_exchange_bindings">>, bool, true},
- {<<"basic.nack">>, bool, true},
- {<<"consumer_cancel_notify">>, bool, true},
- {<<"connection.blocked">>, bool, true},
- {<<"consumer_priorities">>, bool, true},
- {<<"authentication_failure_close">>, bool, true},
- {<<"per_consumer_qos">>, bool, true}];
-server_capabilities(_) ->
- [].
-
-%%--------------------------------------------------------------------------
-
-log(Level, Fmt, Args) -> rabbit_log:log(connection, Level, Fmt, Args).
-
-socket_error(Reason) when is_atom(Reason) ->
- log(error, "Error on AMQP connection ~p: ~s~n",
- [self(), rabbit_misc:format_inet_error(Reason)]);
-socket_error(Reason) ->
- Level =
- case Reason of
- {ssl_upgrade_error, closed} ->
- %% The socket was closed while upgrading to SSL.
- %% This is presumably a TCP healthcheck, so don't log
- %% it unless specified otherwise.
- debug;
- _ ->
- error
- end,
- log(Level, "Error on AMQP connection ~p:~n~p~n", [self(), Reason]).
-
-inet_op(F) -> rabbit_misc:throw_on_error(inet_error, F).
-
-socket_op(Sock, Fun) ->
- case Fun(Sock) of
- {ok, Res} -> Res;
- {error, Reason} -> socket_error(Reason),
- %% NB: this is tcp socket, even in case of ssl
- rabbit_net:fast_close(Sock),
- exit(normal)
- end.
-
-start_connection(Parent, HelperSup, Deb, Sock, SockTransform) ->
- process_flag(trap_exit, true),
- Name = case rabbit_net:connection_string(Sock, inbound) of
- {ok, Str} -> Str;
- {error, enotconn} -> rabbit_net:fast_close(Sock),
- exit(normal);
- {error, Reason} -> socket_error(Reason),
- rabbit_net:fast_close(Sock),
- exit(normal)
- end,
- {ok, HandshakeTimeout} = application:get_env(rabbit, handshake_timeout),
- InitialFrameMax = application:get_env(rabbit, initial_frame_max, ?FRAME_MIN_SIZE),
- ClientSock = socket_op(Sock, SockTransform),
- erlang:send_after(HandshakeTimeout, self(), handshake_timeout),
- {PeerHost, PeerPort, Host, Port} =
- socket_op(Sock, fun (S) -> rabbit_net:socket_ends(S, inbound) end),
- ?store_proc_name(list_to_binary(Name)),
- State = #v1{parent = Parent,
- sock = ClientSock,
- connection = #connection{
- name = list_to_binary(Name),
- host = Host,
- peer_host = PeerHost,
- port = Port,
- peer_port = PeerPort,
- protocol = none,
- user = none,
- timeout_sec = (HandshakeTimeout / 1000),
- frame_max = InitialFrameMax,
- vhost = none,
- client_properties = none,
- capabilities = [],
- auth_mechanism = none,
- auth_state = none,
- connected_at = time_compat:os_system_time(
- milli_seconds)},
- callback = uninitialized_callback,
- recv_len = 0,
- pending_recv = false,
- connection_state = pre_init,
- queue_collector = undefined, %% started on tune-ok
- helper_sup = HelperSup,
- heartbeater = none,
- channel_sup_sup_pid = none,
- channel_count = 0,
- throttle = #throttle{
- alarmed_by = [],
- last_blocked_by = none,
- last_blocked_at = never}},
- try
- run({?MODULE, recvloop,
- [Deb, [], 0, switch_callback(rabbit_event:init_stats_timer(
- State, #v1.stats_timer),
- handshake, 8)]}),
- log(info, "closing AMQP connection ~p (~s)~n", [self(), Name])
- catch
- Ex ->
- log_connection_exception(Name, Ex)
- after
- %% We don't call gen_tcp:close/1 here since it waits for
- %% pending output to be sent, which results in unnecessary
- %% delays. We could just terminate - the reader is the
- %% controlling process and hence its termination will close
- %% the socket. However, to keep the file_handle_cache
- %% accounting as accurate as possible we ought to close the
- %% socket w/o delay before termination.
- rabbit_net:fast_close(ClientSock),
- rabbit_networking:unregister_connection(self()),
- rabbit_event:notify(connection_closed, [{pid, self()}])
- end,
- done.
-
-log_connection_exception(Name, Ex) ->
- Severity = case Ex of
- connection_closed_with_no_data_received -> debug;
- connection_closed_abruptly -> warning;
- _ -> error
- end,
- log_connection_exception(Severity, Name, Ex).
-
-log_connection_exception(Severity, Name, {heartbeat_timeout, TimeoutSec}) ->
- %% Long line to avoid extra spaces and line breaks in log
- log(Severity, "closing AMQP connection ~p (~s):~nMissed heartbeats from client, timeout: ~ps~n",
- [self(), Name, TimeoutSec]);
-log_connection_exception(Severity, Name, Ex) ->
- log(Severity, "closing AMQP connection ~p (~s):~n~p~n",
- [self(), Name, Ex]).
-
-run({M, F, A}) ->
- try apply(M, F, A)
- catch {become, MFA} -> run(MFA)
- end.
-
-recvloop(Deb, Buf, BufLen, State = #v1{pending_recv = true}) ->
- mainloop(Deb, Buf, BufLen, State);
-recvloop(Deb, Buf, BufLen, State = #v1{connection_state = blocked}) ->
- mainloop(Deb, Buf, BufLen, State);
-recvloop(Deb, Buf, BufLen, State = #v1{connection_state = {become, F}}) ->
- throw({become, F(Deb, Buf, BufLen, State)});
-recvloop(Deb, Buf, BufLen, State = #v1{sock = Sock, recv_len = RecvLen})
- when BufLen < RecvLen ->
- case rabbit_net:setopts(Sock, [{active, once}]) of
- ok -> mainloop(Deb, Buf, BufLen,
- State#v1{pending_recv = true});
- {error, Reason} -> stop(Reason, State)
- end;
-recvloop(Deb, [B], _BufLen, State) ->
- {Rest, State1} = handle_input(State#v1.callback, B, State),
- recvloop(Deb, [Rest], size(Rest), State1);
-recvloop(Deb, Buf, BufLen, State = #v1{recv_len = RecvLen}) ->
- {DataLRev, RestLRev} = binlist_split(BufLen - RecvLen, Buf, []),
- Data = list_to_binary(lists:reverse(DataLRev)),
- {<<>>, State1} = handle_input(State#v1.callback, Data, State),
- recvloop(Deb, lists:reverse(RestLRev), BufLen - RecvLen, State1).
-
-binlist_split(0, L, Acc) ->
- {L, Acc};
-binlist_split(Len, L, [Acc0|Acc]) when Len < 0 ->
- {H, T} = split_binary(Acc0, -Len),
- {[H|L], [T|Acc]};
-binlist_split(Len, [H|T], Acc) ->
- binlist_split(Len - size(H), T, [H|Acc]).
-
-mainloop(Deb, Buf, BufLen, State = #v1{sock = Sock,
- connection_state = CS,
- connection = #connection{
- name = ConnName}}) ->
- Recv = rabbit_net:recv(Sock),
- case CS of
- pre_init when Buf =:= [] ->
- %% We only log incoming connections when either the
- %% first byte was received or there was an error (eg. a
- %% timeout).
- %%
- %% The goal is to not log TCP healthchecks (a connection
- %% with no data received) unless specified otherwise.
- log(case Recv of
- closed -> debug;
- _ -> info
- end, "accepting AMQP connection ~p (~s)~n",
- [self(), ConnName]);
- _ ->
- ok
- end,
- case Recv of
- {data, Data} ->
- recvloop(Deb, [Data | Buf], BufLen + size(Data),
- State#v1{pending_recv = false});
- closed when State#v1.connection_state =:= closed ->
- ok;
- closed when CS =:= pre_init andalso Buf =:= [] ->
- stop(tcp_healthcheck, State);
- closed ->
- stop(closed, State);
- {error, Reason} ->
- stop(Reason, State);
- {other, {system, From, Request}} ->
- sys:handle_system_msg(Request, From, State#v1.parent,
- ?MODULE, Deb, {Buf, BufLen, State});
- {other, Other} ->
- case handle_other(Other, State) of
- stop -> ok;
- NewState -> recvloop(Deb, Buf, BufLen, NewState)
- end
- end.
-
-stop(tcp_healthcheck, State) ->
- %% The connection was closed before any packet was received. It's
- %% probably a load-balancer healthcheck: don't consider this a
- %% failure.
- maybe_emit_stats(State),
- throw(connection_closed_with_no_data_received);
-stop(closed, State) ->
- maybe_emit_stats(State),
- throw(connection_closed_abruptly);
-stop(Reason, State) ->
- maybe_emit_stats(State),
- throw({inet_error, Reason}).
-
-handle_other({conserve_resources, Source, Conserve},
- State = #v1{throttle = Throttle = #throttle{alarmed_by = CR}}) ->
- CR1 = case Conserve of
- true -> lists:usort([Source | CR]);
- false -> CR -- [Source]
- end,
- State1 = control_throttle(
- State#v1{throttle = Throttle#throttle{alarmed_by = CR1}}),
- case {blocked_by_alarm(State), blocked_by_alarm(State1)} of
- {false, true} -> ok = send_blocked(State1);
- {true, false} -> ok = send_unblocked(State1);
- {_, _} -> ok
- end,
- State1;
-handle_other({channel_closing, ChPid}, State) ->
- ok = rabbit_channel:ready_for_close(ChPid),
- {_, State1} = channel_cleanup(ChPid, State),
- maybe_close(control_throttle(State1));
-handle_other({'EXIT', Parent, Reason}, State = #v1{parent = Parent}) ->
- terminate(io_lib:format("broker forced connection closure "
- "with reason '~w'", [Reason]), State),
- %% this is what we are expected to do according to
- %% http://www.erlang.org/doc/man/sys.html
- %%
- %% If we wanted to be *really* nice we should wait for a while for
- %% clients to close the socket at their end, just as we do in the
- %% ordinary error case. However, since this termination is
- %% initiated by our parent it is probably more important to exit
- %% quickly.
- maybe_emit_stats(State),
- exit(Reason);
-handle_other({channel_exit, _Channel, E = {writer, send_failed, _E}}, State) ->
- maybe_emit_stats(State),
- throw(E);
-handle_other({channel_exit, Channel, Reason}, State) ->
- handle_exception(State, Channel, Reason);
-handle_other({'DOWN', _MRef, process, ChPid, Reason}, State) ->
- handle_dependent_exit(ChPid, Reason, State);
-handle_other(terminate_connection, State) ->
- maybe_emit_stats(State),
- stop;
-handle_other(handshake_timeout, State)
- when ?IS_RUNNING(State) orelse ?IS_STOPPING(State) ->
- State;
-handle_other(handshake_timeout, State) ->
- maybe_emit_stats(State),
- throw({handshake_timeout, State#v1.callback});
-handle_other(heartbeat_timeout, State = #v1{connection_state = closed}) ->
- State;
-handle_other(heartbeat_timeout,
- State = #v1{connection = #connection{timeout_sec = T}}) ->
- maybe_emit_stats(State),
- throw({heartbeat_timeout, T});
-handle_other({'$gen_call', From, {shutdown, Explanation}}, State) ->
- {ForceTermination, NewState} = terminate(Explanation, State),
- gen_server:reply(From, ok),
- case ForceTermination of
- force -> stop;
- normal -> NewState
- end;
-handle_other({'$gen_call', From, info}, State) ->
- gen_server:reply(From, infos(?INFO_KEYS, State)),
- State;
-handle_other({'$gen_call', From, {info, Items}}, State) ->
- gen_server:reply(From, try {ok, infos(Items, State)}
- catch Error -> {error, Error}
- end),
- State;
-handle_other({'$gen_cast', {force_event_refresh, Ref}}, State)
- when ?IS_RUNNING(State) ->
- rabbit_event:notify(
- connection_created,
- [{type, network} | infos(?CREATION_EVENT_KEYS, State)], Ref),
- rabbit_event:init_stats_timer(State, #v1.stats_timer);
-handle_other({'$gen_cast', {force_event_refresh, _Ref}}, State) ->
- %% Ignore, we will emit a created event once we start running.
- State;
-handle_other(ensure_stats, State) ->
- ensure_stats_timer(State);
-handle_other(emit_stats, State) ->
- emit_stats(State);
-handle_other({bump_credit, Msg}, State) ->
- %% Here we are receiving credit by some channel process.
- credit_flow:handle_bump_msg(Msg),
- control_throttle(State);
-handle_other(Other, State) ->
- %% internal error -> something worth dying for
- maybe_emit_stats(State),
- exit({unexpected_message, Other}).
-
-switch_callback(State, Callback, Length) ->
- State#v1{callback = Callback, recv_len = Length}.
-
-terminate(Explanation, State) when ?IS_RUNNING(State) ->
- {normal, handle_exception(State, 0,
- rabbit_misc:amqp_error(
- connection_forced, Explanation, [], none))};
-terminate(_Explanation, State) ->
- {force, State}.
-
-control_throttle(State = #v1{connection_state = CS, throttle = Throttle}) ->
- IsThrottled = ((Throttle#throttle.alarmed_by =/= []) orelse
- credit_flow:blocked()),
- case {CS, IsThrottled} of
- {running, true} -> State#v1{connection_state = blocking};
- {blocking, false} -> State#v1{connection_state = running};
- {blocked, false} -> ok = rabbit_heartbeat:resume_monitor(
- State#v1.heartbeater),
- State#v1{connection_state = running};
- {blocked, true} -> State#v1{throttle = update_last_blocked_by(
- Throttle)};
- {_, _} -> State
- end.
-
-maybe_block(State = #v1{connection_state = blocking,
- throttle = Throttle}) ->
- ok = rabbit_heartbeat:pause_monitor(State#v1.heartbeater),
- State1 = State#v1{connection_state = blocked,
- throttle = update_last_blocked_by(
- Throttle#throttle{
- last_blocked_at =
- time_compat:monotonic_time()})},
- case {blocked_by_alarm(State), blocked_by_alarm(State1)} of
- {false, true} -> ok = send_blocked(State1);
- {_, _} -> ok
- end,
- State1;
-maybe_block(State) ->
- State.
-
-
-blocked_by_alarm(#v1{connection_state = blocked,
- throttle = #throttle{alarmed_by = CR}})
- when CR =/= [] ->
- true;
-blocked_by_alarm(#v1{}) ->
- false.
-
-send_blocked(#v1{throttle = #throttle{alarmed_by = CR},
- connection = #connection{protocol = Protocol,
- capabilities = Capabilities},
- sock = Sock}) ->
- case rabbit_misc:table_lookup(Capabilities, <<"connection.blocked">>) of
- {bool, true} ->
- RStr = string:join([atom_to_list(A) || A <- CR], " & "),
- Reason = list_to_binary(rabbit_misc:format("low on ~s", [RStr])),
- ok = send_on_channel0(Sock, #'connection.blocked'{reason = Reason},
- Protocol);
- _ ->
- ok
- end.
-
-send_unblocked(#v1{connection = #connection{protocol = Protocol,
- capabilities = Capabilities},
- sock = Sock}) ->
- case rabbit_misc:table_lookup(Capabilities, <<"connection.blocked">>) of
- {bool, true} ->
- ok = send_on_channel0(Sock, #'connection.unblocked'{}, Protocol);
- _ ->
- ok
- end.
-
-update_last_blocked_by(Throttle = #throttle{alarmed_by = []}) ->
- Throttle#throttle{last_blocked_by = flow};
-update_last_blocked_by(Throttle) ->
- Throttle#throttle{last_blocked_by = resource}.
-
-%%--------------------------------------------------------------------------
-%% error handling / termination
-
-close_connection(State = #v1{queue_collector = Collector,
- connection = #connection{
- timeout_sec = TimeoutSec}}) ->
- %% The spec says "Exclusive queues may only be accessed by the
- %% current connection, and are deleted when that connection
- %% closes." This does not strictly imply synchrony, but in
- %% practice it seems to be what people assume.
- rabbit_queue_collector:delete_all(Collector),
- %% We terminate the connection after the specified interval, but
- %% no later than ?CLOSING_TIMEOUT seconds.
- erlang:send_after((if TimeoutSec > 0 andalso
- TimeoutSec < ?CLOSING_TIMEOUT -> TimeoutSec;
- true -> ?CLOSING_TIMEOUT
- end) * 1000, self(), terminate_connection),
- State#v1{connection_state = closed}.
-
-handle_dependent_exit(ChPid, Reason, State) ->
- {Channel, State1} = channel_cleanup(ChPid, State),
- case {Channel, termination_kind(Reason)} of
- {undefined, controlled} -> State1;
- {undefined, uncontrolled} -> exit({abnormal_dependent_exit,
- ChPid, Reason});
- {_, controlled} -> maybe_close(control_throttle(State1));
- {_, uncontrolled} -> State2 = handle_exception(
- State1, Channel, Reason),
- maybe_close(control_throttle(State2))
- end.
-
-terminate_channels(#v1{channel_count = 0} = State) ->
- State;
-terminate_channels(#v1{channel_count = ChannelCount} = State) ->
- lists:foreach(fun rabbit_channel:shutdown/1, all_channels()),
- Timeout = 1000 * ?CHANNEL_TERMINATION_TIMEOUT * ChannelCount,
- TimerRef = erlang:send_after(Timeout, self(), cancel_wait),
- wait_for_channel_termination(ChannelCount, TimerRef, State).
-
-wait_for_channel_termination(0, TimerRef, State) ->
- case erlang:cancel_timer(TimerRef) of
- false -> receive
- cancel_wait -> State
- end;
- _ -> State
- end;
-wait_for_channel_termination(N, TimerRef,
- State = #v1{connection_state = CS,
- connection = #connection{
- name = ConnName,
- user = User,
- vhost = VHost}}) ->
- receive
- {'DOWN', _MRef, process, ChPid, Reason} ->
- {Channel, State1} = channel_cleanup(ChPid, State),
- case {Channel, termination_kind(Reason)} of
- {undefined, _} ->
- exit({abnormal_dependent_exit, ChPid, Reason});
- {_, controlled} ->
- wait_for_channel_termination(N-1, TimerRef, State1);
- {_, uncontrolled} ->
- log(error, "Error on AMQP connection ~p (~s, vhost: '~s',"
- " user: '~s', state: ~p), channel ~p:"
- "error while terminating:~n~p~n",
- [self(), ConnName, VHost, User#user.username,
- CS, Channel, Reason]),
- wait_for_channel_termination(N-1, TimerRef, State1)
- end;
- cancel_wait ->
- exit(channel_termination_timeout)
- end.
-
-maybe_close(State = #v1{connection_state = closing,
- channel_count = 0,
- connection = #connection{protocol = Protocol},
- sock = Sock}) ->
- NewState = close_connection(State),
- ok = send_on_channel0(Sock, #'connection.close_ok'{}, Protocol),
- NewState;
-maybe_close(State) ->
- State.
-
-termination_kind(normal) -> controlled;
-termination_kind(_) -> uncontrolled.
-
-log_hard_error(#v1{connection_state = CS,
- connection = #connection{
- name = ConnName,
- user = User,
- vhost = VHost}}, Channel, Reason) ->
- log(error,
- "Error on AMQP connection ~p (~s, vhost: '~s',"
- " user: '~s', state: ~p), channel ~p:~n~p~n",
- [self(), ConnName, VHost, User#user.username, CS, Channel, Reason]).
-
-handle_exception(State = #v1{connection_state = closed}, Channel, Reason) ->
- log_hard_error(State, Channel, Reason),
- State;
-handle_exception(State = #v1{connection = #connection{protocol = Protocol},
- connection_state = CS},
- Channel, Reason)
- when ?IS_RUNNING(State) orelse CS =:= closing ->
- log_hard_error(State, Channel, Reason),
- {0, CloseMethod} =
- rabbit_binary_generator:map_exception(Channel, Reason, Protocol),
- State1 = close_connection(terminate_channels(State)),
- ok = send_on_channel0(State1#v1.sock, CloseMethod, Protocol),
- State1;
-handle_exception(State, Channel, Reason) ->
- %% We don't trust the client at this point - force them to wait
- %% for a bit so they can't DOS us with repeated failed logins etc.
- timer:sleep(?SILENT_CLOSE_DELAY * 1000),
- throw({handshake_error, State#v1.connection_state, Channel, Reason}).
-
-%% we've "lost sync" with the client and hence must not accept any
-%% more input
-fatal_frame_error(Error, Type, Channel, Payload, State) ->
- frame_error(Error, Type, Channel, Payload, State),
- %% grace period to allow transmission of error
- timer:sleep(?SILENT_CLOSE_DELAY * 1000),
- throw(fatal_frame_error).
-
-frame_error(Error, Type, Channel, Payload, State) ->
- {Str, Bin} = payload_snippet(Payload),
- handle_exception(State, Channel,
- rabbit_misc:amqp_error(frame_error,
- "type ~p, ~s octets = ~p: ~p",
- [Type, Str, Bin, Error], none)).
-
-unexpected_frame(Type, Channel, Payload, State) ->
- {Str, Bin} = payload_snippet(Payload),
- handle_exception(State, Channel,
- rabbit_misc:amqp_error(unexpected_frame,
- "type ~p, ~s octets = ~p",
- [Type, Str, Bin], none)).
-
-payload_snippet(Payload) when size(Payload) =< 16 ->
- {"all", Payload};
-payload_snippet(<<Snippet:16/binary, _/binary>>) ->
- {"first 16", Snippet}.
-
-%%--------------------------------------------------------------------------
-
-create_channel(_Channel,
- #v1{channel_count = ChannelCount,
- connection = #connection{channel_max = ChannelMax}})
- when ChannelMax /= 0 andalso ChannelCount >= ChannelMax ->
- {error, rabbit_misc:amqp_error(
- not_allowed, "number of channels opened (~w) has reached the "
- "negotiated channel_max (~w)",
- [ChannelCount, ChannelMax], 'none')};
-create_channel(Channel,
- #v1{sock = Sock,
- queue_collector = Collector,
- channel_sup_sup_pid = ChanSupSup,
- channel_count = ChannelCount,
- connection =
- #connection{name = Name,
- protocol = Protocol,
- frame_max = FrameMax,
- user = User,
- vhost = VHost,
- capabilities = Capabilities}} = State) ->
- {ok, _ChSupPid, {ChPid, AState}} =
- rabbit_channel_sup_sup:start_channel(
- ChanSupSup, {tcp, Sock, Channel, FrameMax, self(), Name,
- Protocol, User, VHost, Capabilities, Collector}),
- MRef = erlang:monitor(process, ChPid),
- put({ch_pid, ChPid}, {Channel, MRef}),
- put({channel, Channel}, {ChPid, AState}),
- {ok, {ChPid, AState}, State#v1{channel_count = ChannelCount + 1}}.
-
-channel_cleanup(ChPid, State = #v1{channel_count = ChannelCount}) ->
- case get({ch_pid, ChPid}) of
- undefined -> {undefined, State};
- {Channel, MRef} -> credit_flow:peer_down(ChPid),
- erase({channel, Channel}),
- erase({ch_pid, ChPid}),
- erlang:demonitor(MRef, [flush]),
- {Channel, State#v1{channel_count = ChannelCount - 1}}
- end.
-
-all_channels() -> [ChPid || {{ch_pid, ChPid}, _ChannelMRef} <- get()].
-
-%%--------------------------------------------------------------------------
-
-handle_frame(Type, 0, Payload,
- State = #v1{connection = #connection{protocol = Protocol}})
- when ?IS_STOPPING(State) ->
- case rabbit_command_assembler:analyze_frame(Type, Payload, Protocol) of
- {method, MethodName, FieldsBin} ->
- handle_method0(MethodName, FieldsBin, State);
- _Other -> State
- end;
-handle_frame(Type, 0, Payload,
- State = #v1{connection = #connection{protocol = Protocol}}) ->
- case rabbit_command_assembler:analyze_frame(Type, Payload, Protocol) of
- error -> frame_error(unknown_frame, Type, 0, Payload, State);
- heartbeat -> State;
- {method, MethodName, FieldsBin} ->
- handle_method0(MethodName, FieldsBin, State);
- _Other -> unexpected_frame(Type, 0, Payload, State)
- end;
-handle_frame(Type, Channel, Payload,
- State = #v1{connection = #connection{protocol = Protocol}})
- when ?IS_RUNNING(State) ->
- case rabbit_command_assembler:analyze_frame(Type, Payload, Protocol) of
- error -> frame_error(unknown_frame, Type, Channel, Payload, State);
- heartbeat -> unexpected_frame(Type, Channel, Payload, State);
- Frame -> process_frame(Frame, Channel, State)
- end;
-handle_frame(_Type, _Channel, _Payload, State) when ?IS_STOPPING(State) ->
- State;
-handle_frame(Type, Channel, Payload, State) ->
- unexpected_frame(Type, Channel, Payload, State).
-
-process_frame(Frame, Channel, State) ->
- ChKey = {channel, Channel},
- case (case get(ChKey) of
- undefined -> create_channel(Channel, State);
- Other -> {ok, Other, State}
- end) of
- {error, Error} ->
- handle_exception(State, Channel, Error);
- {ok, {ChPid, AState}, State1} ->
- case rabbit_command_assembler:process(Frame, AState) of
- {ok, NewAState} ->
- put(ChKey, {ChPid, NewAState}),
- post_process_frame(Frame, ChPid, State1);
- {ok, Method, NewAState} ->
- rabbit_channel:do(ChPid, Method),
- put(ChKey, {ChPid, NewAState}),
- post_process_frame(Frame, ChPid, State1);
- {ok, Method, Content, NewAState} ->
- rabbit_channel:do_flow(ChPid, Method, Content),
- put(ChKey, {ChPid, NewAState}),
- post_process_frame(Frame, ChPid, control_throttle(State1));
- {error, Reason} ->
- handle_exception(State1, Channel, Reason)
- end
- end.
-
-post_process_frame({method, 'channel.close_ok', _}, ChPid, State) ->
- {_, State1} = channel_cleanup(ChPid, State),
- %% This is not strictly necessary, but more obviously
- %% correct. Also note that we do not need to call maybe_close/1
- %% since we cannot possibly be in the 'closing' state.
- control_throttle(State1);
-post_process_frame({content_header, _, _, _, _}, _ChPid, State) ->
- maybe_block(State);
-post_process_frame({content_body, _}, _ChPid, State) ->
- maybe_block(State);
-post_process_frame(_Frame, _ChPid, State) ->
- State.
-
-%%--------------------------------------------------------------------------
-
-%% We allow clients to exceed the frame size a little bit since quite
-%% a few get it wrong - off-by 1 or 8 (empty frame size) are typical.
--define(FRAME_SIZE_FUDGE, ?EMPTY_FRAME_SIZE).
-
-handle_input(frame_header, <<Type:8,Channel:16,PayloadSize:32, _/binary>>,
- State = #v1{connection = #connection{frame_max = FrameMax}})
- when FrameMax /= 0 andalso
- PayloadSize > FrameMax - ?EMPTY_FRAME_SIZE + ?FRAME_SIZE_FUDGE ->
- fatal_frame_error(
- {frame_too_large, PayloadSize, FrameMax - ?EMPTY_FRAME_SIZE},
- Type, Channel, <<>>, State);
-handle_input(frame_header, <<Type:8,Channel:16,PayloadSize:32,
- Payload:PayloadSize/binary, ?FRAME_END,
- Rest/binary>>,
- State) ->
- {Rest, ensure_stats_timer(handle_frame(Type, Channel, Payload, State))};
-handle_input(frame_header, <<Type:8,Channel:16,PayloadSize:32, Rest/binary>>,
- State) ->
- {Rest, ensure_stats_timer(
- switch_callback(State,
- {frame_payload, Type, Channel, PayloadSize},
- PayloadSize + 1))};
-handle_input({frame_payload, Type, Channel, PayloadSize}, Data, State) ->
- <<Payload:PayloadSize/binary, EndMarker, Rest/binary>> = Data,
- case EndMarker of
- ?FRAME_END -> State1 = handle_frame(Type, Channel, Payload, State),
- {Rest, switch_callback(State1, frame_header, 7)};
- _ -> fatal_frame_error({invalid_frame_end_marker, EndMarker},
- Type, Channel, Payload, State)
- end;
-handle_input(handshake, <<"AMQP", A, B, C, D, Rest/binary>>, State) ->
- {Rest, handshake({A, B, C, D}, State)};
-handle_input(handshake, <<Other:8/binary, _/binary>>, #v1{sock = Sock}) ->
- refuse_connection(Sock, {bad_header, Other});
-handle_input(Callback, Data, _State) ->
- throw({bad_input, Callback, Data}).
-
-%% The two rules pertaining to version negotiation:
-%%
-%% * If the server cannot support the protocol specified in the
-%% protocol header, it MUST respond with a valid protocol header and
-%% then close the socket connection.
-%%
-%% * The server MUST provide a protocol version that is lower than or
-%% equal to that requested by the client in the protocol header.
-handshake({0, 0, 9, 1}, State) ->
- start_connection({0, 9, 1}, rabbit_framing_amqp_0_9_1, State);
-
-%% This is the protocol header for 0-9, which we can safely treat as
-%% though it were 0-9-1.
-handshake({1, 1, 0, 9}, State) ->
- start_connection({0, 9, 0}, rabbit_framing_amqp_0_9_1, State);
-
-%% This is what most clients send for 0-8. The 0-8 spec, confusingly,
-%% defines the version as 8-0.
-handshake({1, 1, 8, 0}, State) ->
- start_connection({8, 0, 0}, rabbit_framing_amqp_0_8, State);
-
-%% The 0-8 spec as on the AMQP web site actually has this as the
-%% protocol header; some libraries e.g., py-amqplib, send it when they
-%% want 0-8.
-handshake({1, 1, 9, 1}, State) ->
- start_connection({8, 0, 0}, rabbit_framing_amqp_0_8, State);
-
-%% ... and finally, the 1.0 spec is crystal clear!
-handshake({Id, 1, 0, 0}, State) ->
- become_1_0(Id, State);
-
-handshake(Vsn, #v1{sock = Sock}) ->
- refuse_connection(Sock, {bad_version, Vsn}).
-
-%% Offer a protocol version to the client. Connection.start only
-%% includes a major and minor version number, Luckily 0-9 and 0-9-1
-%% are similar enough that clients will be happy with either.
-start_connection({ProtocolMajor, ProtocolMinor, _ProtocolRevision},
- Protocol,
- State = #v1{sock = Sock, connection = Connection}) ->
- rabbit_networking:register_connection(self()),
- Start = #'connection.start'{
- version_major = ProtocolMajor,
- version_minor = ProtocolMinor,
- server_properties = server_properties(Protocol),
- mechanisms = auth_mechanisms_binary(Sock),
- locales = <<"en_US">> },
- ok = send_on_channel0(Sock, Start, Protocol),
- switch_callback(State#v1{connection = Connection#connection{
- timeout_sec = ?NORMAL_TIMEOUT,
- protocol = Protocol},
- connection_state = starting},
- frame_header, 7).
-
-refuse_connection(Sock, Exception, {A, B, C, D}) ->
- ok = inet_op(fun () -> rabbit_net:send(Sock, <<"AMQP",A,B,C,D>>) end),
- throw(Exception).
-
--ifdef(use_specs).
--spec(refuse_connection/2 :: (rabbit_net:socket(), any()) -> no_return()).
--endif.
-refuse_connection(Sock, Exception) ->
- refuse_connection(Sock, Exception, {0, 0, 9, 1}).
-
-ensure_stats_timer(State = #v1{connection_state = running}) ->
- rabbit_event:ensure_stats_timer(State, #v1.stats_timer, emit_stats);
-ensure_stats_timer(State) ->
- State.
-
-%%--------------------------------------------------------------------------
-
-handle_method0(MethodName, FieldsBin,
- State = #v1{connection = #connection{protocol = Protocol}}) ->
- try
- handle_method0(Protocol:decode_method_fields(MethodName, FieldsBin),
- State)
- catch throw:{inet_error, E} when E =:= closed; E =:= enotconn ->
- maybe_emit_stats(State),
- throw(connection_closed_abruptly);
- exit:#amqp_error{method = none} = Reason ->
- handle_exception(State, 0, Reason#amqp_error{method = MethodName});
- Type:Reason ->
- Stack = erlang:get_stacktrace(),
- handle_exception(State, 0, {Type, Reason, MethodName, Stack})
- end.
-
-handle_method0(#'connection.start_ok'{mechanism = Mechanism,
- response = Response,
- client_properties = ClientProperties},
- State0 = #v1{connection_state = starting,
- connection = Connection,
- sock = Sock}) ->
- AuthMechanism = auth_mechanism_to_module(Mechanism, Sock),
- Capabilities =
- case rabbit_misc:table_lookup(ClientProperties, <<"capabilities">>) of
- {table, Capabilities1} -> Capabilities1;
- _ -> []
- end,
- State = State0#v1{connection_state = securing,
- connection =
- Connection#connection{
- client_properties = ClientProperties,
- capabilities = Capabilities,
- auth_mechanism = {Mechanism, AuthMechanism},
- auth_state = AuthMechanism:init(Sock)}},
- auth_phase(Response, State);
-
-handle_method0(#'connection.secure_ok'{response = Response},
- State = #v1{connection_state = securing}) ->
- auth_phase(Response, State);
-
-handle_method0(#'connection.tune_ok'{frame_max = FrameMax,
- channel_max = ChannelMax,
- heartbeat = ClientHeartbeat},
- State = #v1{connection_state = tuning,
- connection = Connection,
- helper_sup = SupPid,
- sock = Sock}) ->
- ok = validate_negotiated_integer_value(
- frame_max, ?FRAME_MIN_SIZE, FrameMax),
- ok = validate_negotiated_integer_value(
- channel_max, ?CHANNEL_MIN, ChannelMax),
- {ok, Collector} = rabbit_connection_helper_sup:start_queue_collector(
- SupPid, Connection#connection.name),
- Frame = rabbit_binary_generator:build_heartbeat_frame(),
- SendFun = fun() -> catch rabbit_net:send(Sock, Frame) end,
- Parent = self(),
- ReceiveFun = fun() -> Parent ! heartbeat_timeout end,
- Heartbeater = rabbit_heartbeat:start(
- SupPid, Sock, Connection#connection.name,
- ClientHeartbeat, SendFun, ClientHeartbeat, ReceiveFun),
- State#v1{connection_state = opening,
- connection = Connection#connection{
- frame_max = FrameMax,
- channel_max = ChannelMax,
- timeout_sec = ClientHeartbeat},
- queue_collector = Collector,
- heartbeater = Heartbeater};
-
-handle_method0(#'connection.open'{virtual_host = VHostPath},
- State = #v1{connection_state = opening,
- connection = Connection = #connection{
- user = User,
- protocol = Protocol},
- helper_sup = SupPid,
- sock = Sock,
- throttle = Throttle}) ->
- ok = rabbit_access_control:check_vhost_access(User, VHostPath, Sock),
- NewConnection = Connection#connection{vhost = VHostPath},
- ok = send_on_channel0(Sock, #'connection.open_ok'{}, Protocol),
- Conserve = rabbit_alarm:register(self(), {?MODULE, conserve_resources, []}),
- Throttle1 = Throttle#throttle{alarmed_by = Conserve},
- {ok, ChannelSupSupPid} =
- rabbit_connection_helper_sup:start_channel_sup_sup(SupPid),
- State1 = control_throttle(
- State#v1{connection_state = running,
- connection = NewConnection,
- channel_sup_sup_pid = ChannelSupSupPid,
- throttle = Throttle1}),
- rabbit_event:notify(connection_created,
- [{type, network} |
- infos(?CREATION_EVENT_KEYS, State1)]),
- maybe_emit_stats(State1),
- State1;
-handle_method0(#'connection.close'{}, State) when ?IS_RUNNING(State) ->
- lists:foreach(fun rabbit_channel:shutdown/1, all_channels()),
- maybe_close(State#v1{connection_state = closing});
-handle_method0(#'connection.close'{},
- State = #v1{connection = #connection{protocol = Protocol},
- sock = Sock})
- when ?IS_STOPPING(State) ->
- %% We're already closed or closing, so we don't need to cleanup
- %% anything.
- ok = send_on_channel0(Sock, #'connection.close_ok'{}, Protocol),
- State;
-handle_method0(#'connection.close_ok'{},
- State = #v1{connection_state = closed}) ->
- self() ! terminate_connection,
- State;
-handle_method0(_Method, State) when ?IS_STOPPING(State) ->
- State;
-handle_method0(_Method, #v1{connection_state = S}) ->
- rabbit_misc:protocol_error(
- channel_error, "unexpected method in connection state ~w", [S]).
-
-validate_negotiated_integer_value(Field, Min, ClientValue) ->
- ServerValue = get_env(Field),
- if ClientValue /= 0 andalso ClientValue < Min ->
- fail_negotiation(Field, min, ServerValue, ClientValue);
- ServerValue /= 0 andalso (ClientValue =:= 0 orelse
- ClientValue > ServerValue) ->
- fail_negotiation(Field, max, ServerValue, ClientValue);
- true ->
- ok
- end.
-
-%% keep dialyzer happy
--spec fail_negotiation(atom(), 'min' | 'max', integer(), integer()) ->
- no_return().
-fail_negotiation(Field, MinOrMax, ServerValue, ClientValue) ->
- {S1, S2} = case MinOrMax of
- min -> {lower, minimum};
- max -> {higher, maximum}
- end,
- rabbit_misc:protocol_error(
- not_allowed, "negotiated ~w = ~w is ~w than the ~w allowed value (~w)",
- [Field, ClientValue, S1, S2, ServerValue], 'connection.tune').
-
-get_env(Key) ->
- {ok, Value} = application:get_env(rabbit, Key),
- Value.
-
-send_on_channel0(Sock, Method, Protocol) ->
- ok = rabbit_writer:internal_send_command(Sock, 0, Method, Protocol).
-
-auth_mechanism_to_module(TypeBin, Sock) ->
- case rabbit_registry:binary_to_type(TypeBin) of
- {error, not_found} ->
- rabbit_misc:protocol_error(
- command_invalid, "unknown authentication mechanism '~s'",
- [TypeBin]);
- T ->
- case {lists:member(T, auth_mechanisms(Sock)),
- rabbit_registry:lookup_module(auth_mechanism, T)} of
- {true, {ok, Module}} ->
- Module;
- _ ->
- rabbit_misc:protocol_error(
- command_invalid,
- "invalid authentication mechanism '~s'", [T])
- end
- end.
-
-auth_mechanisms(Sock) ->
- {ok, Configured} = application:get_env(auth_mechanisms),
- [Name || {Name, Module} <- rabbit_registry:lookup_all(auth_mechanism),
- Module:should_offer(Sock), lists:member(Name, Configured)].
-
-auth_mechanisms_binary(Sock) ->
- list_to_binary(
- string:join([atom_to_list(A) || A <- auth_mechanisms(Sock)], " ")).
-
-auth_phase(Response,
- State = #v1{connection = Connection =
- #connection{protocol = Protocol,
- auth_mechanism = {Name, AuthMechanism},
- auth_state = AuthState},
- sock = Sock}) ->
- case AuthMechanism:handle_response(Response, AuthState) of
- {refused, Username, Msg, Args} ->
- auth_fail(Username, Msg, Args, Name, State);
- {protocol_error, Msg, Args} ->
- notify_auth_result(none, user_authentication_failure,
- [{error, rabbit_misc:format(Msg, Args)}],
- State),
- rabbit_misc:protocol_error(syntax_error, Msg, Args);
- {challenge, Challenge, AuthState1} ->
- Secure = #'connection.secure'{challenge = Challenge},
- ok = send_on_channel0(Sock, Secure, Protocol),
- State#v1{connection = Connection#connection{
- auth_state = AuthState1}};
- {ok, User = #user{username = Username}} ->
- case rabbit_access_control:check_user_loopback(Username, Sock) of
- ok ->
- notify_auth_result(Username, user_authentication_success,
- [], State);
- not_allowed ->
- auth_fail(Username, "user '~s' can only connect via "
- "localhost", [Username], Name, State)
- end,
- Tune = #'connection.tune'{frame_max = get_env(frame_max),
- channel_max = get_env(channel_max),
- heartbeat = get_env(heartbeat)},
- ok = send_on_channel0(Sock, Tune, Protocol),
- State#v1{connection_state = tuning,
- connection = Connection#connection{user = User,
- auth_state = none}}
- end.
-
--ifdef(use_specs).
--spec(auth_fail/5 ::
- (rabbit_types:username() | none, string(), [any()], binary(), #v1{}) ->
- no_return()).
--endif.
-auth_fail(Username, Msg, Args, AuthName,
- State = #v1{connection = #connection{protocol = Protocol,
- capabilities = Capabilities}}) ->
- notify_auth_result(Username, user_authentication_failure,
- [{error, rabbit_misc:format(Msg, Args)}], State),
- AmqpError = rabbit_misc:amqp_error(
- access_refused, "~s login refused: ~s",
- [AuthName, io_lib:format(Msg, Args)], none),
- case rabbit_misc:table_lookup(Capabilities,
- <<"authentication_failure_close">>) of
- {bool, true} ->
- SafeMsg = io_lib:format(
- "Login was refused using authentication "
- "mechanism ~s. For details see the broker "
- "logfile.", [AuthName]),
- AmqpError1 = AmqpError#amqp_error{explanation = SafeMsg},
- {0, CloseMethod} = rabbit_binary_generator:map_exception(
- 0, AmqpError1, Protocol),
- ok = send_on_channel0(State#v1.sock, CloseMethod, Protocol);
- _ -> ok
- end,
- rabbit_misc:protocol_error(AmqpError).
-
-notify_auth_result(Username, AuthResult, ExtraProps, State) ->
- EventProps = [{connection_type, network},
- {name, case Username of none -> ''; _ -> Username end}] ++
- [case Item of
- name -> {connection_name, i(name, State)};
- _ -> {Item, i(Item, State)}
- end || Item <- ?AUTH_NOTIFICATION_INFO_KEYS] ++
- ExtraProps,
- rabbit_event:notify(AuthResult, [P || {_, V} = P <- EventProps, V =/= '']).
-
-%%--------------------------------------------------------------------------
-
-infos(Items, State) -> [{Item, i(Item, State)} || Item <- Items].
-
-i(pid, #v1{}) -> self();
-i(SockStat, S) when SockStat =:= recv_oct;
- SockStat =:= recv_cnt;
- SockStat =:= send_oct;
- SockStat =:= send_cnt;
- SockStat =:= send_pend ->
- socket_info(fun (Sock) -> rabbit_net:getstat(Sock, [SockStat]) end,
- fun ([{_, I}]) -> I end, S);
-i(ssl, #v1{sock = Sock}) -> rabbit_net:is_ssl(Sock);
-i(ssl_protocol, S) -> ssl_info(fun ({P, _}) -> P end, S);
-i(ssl_key_exchange, S) -> ssl_info(fun ({_, {K, _, _}}) -> K end, S);
-i(ssl_cipher, S) -> ssl_info(fun ({_, {_, C, _}}) -> C end, S);
-i(ssl_hash, S) -> ssl_info(fun ({_, {_, _, H}}) -> H end, S);
-i(peer_cert_issuer, S) -> cert_info(fun rabbit_ssl:peer_cert_issuer/1, S);
-i(peer_cert_subject, S) -> cert_info(fun rabbit_ssl:peer_cert_subject/1, S);
-i(peer_cert_validity, S) -> cert_info(fun rabbit_ssl:peer_cert_validity/1, S);
-i(channels, #v1{channel_count = ChannelCount}) -> ChannelCount;
-i(state, #v1{connection_state = ConnectionState,
- throttle = #throttle{alarmed_by = Alarms,
- last_blocked_by = WasBlockedBy,
- last_blocked_at = T}}) ->
- case Alarms =:= [] andalso %% not throttled by resource alarms
- (credit_flow:blocked() %% throttled by flow now
- orelse %% throttled by flow recently
- (WasBlockedBy =:= flow andalso T =/= never andalso
- time_compat:convert_time_unit(time_compat:monotonic_time() - T,
- native,
- micro_seconds) < 5000000)) of
- true -> flow;
- false -> ConnectionState
- end;
-i(Item, #v1{connection = Conn}) -> ic(Item, Conn).
-
-ic(name, #connection{name = Name}) -> Name;
-ic(host, #connection{host = Host}) -> Host;
-ic(peer_host, #connection{peer_host = PeerHost}) -> PeerHost;
-ic(port, #connection{port = Port}) -> Port;
-ic(peer_port, #connection{peer_port = PeerPort}) -> PeerPort;
-ic(protocol, #connection{protocol = none}) -> none;
-ic(protocol, #connection{protocol = P}) -> P:version();
-ic(user, #connection{user = none}) -> '';
-ic(user, #connection{user = U}) -> U#user.username;
-ic(vhost, #connection{vhost = VHost}) -> VHost;
-ic(timeout, #connection{timeout_sec = Timeout}) -> Timeout;
-ic(frame_max, #connection{frame_max = FrameMax}) -> FrameMax;
-ic(channel_max, #connection{channel_max = ChMax}) -> ChMax;
-ic(client_properties, #connection{client_properties = CP}) -> CP;
-ic(auth_mechanism, #connection{auth_mechanism = none}) -> none;
-ic(auth_mechanism, #connection{auth_mechanism = {Name, _Mod}}) -> Name;
-ic(connected_at, #connection{connected_at = T}) -> T;
-ic(Item, #connection{}) -> throw({bad_argument, Item}).
-
-socket_info(Get, Select, #v1{sock = Sock}) ->
- case Get(Sock) of
- {ok, T} -> Select(T);
- {error, _} -> ''
- end.
-
-ssl_info(F, #v1{sock = Sock}) ->
- case rabbit_net:ssl_info(Sock) of
- nossl -> '';
- {error, _} -> '';
- {ok, Items} ->
- P = proplists:get_value(protocol, Items),
- CS = proplists:get_value(cipher_suite, Items),
- %% The first form is R14.
- %% The second is R13 - the extra term is exportability (by
- %% inspection, the docs are wrong).
- case CS of
- {K, C, H} -> F({P, {K, C, H}});
- {K, C, H, _} -> F({P, {K, C, H}})
- end
- end.
-
-cert_info(F, #v1{sock = Sock}) ->
- case rabbit_net:peercert(Sock) of
- nossl -> '';
- {error, _} -> '';
- {ok, Cert} -> list_to_binary(F(Cert))
- end.
-
-maybe_emit_stats(State) ->
- rabbit_event:if_enabled(State, #v1.stats_timer,
- fun() -> emit_stats(State) end).
-
-emit_stats(State) ->
- Infos = infos(?STATISTICS_KEYS, State),
- rabbit_event:notify(connection_stats, Infos),
- State1 = rabbit_event:reset_stats_timer(State, #v1.stats_timer),
- %% If we emit an event which looks like we are in flow control, it's not a
- %% good idea for it to be our last even if we go idle. Keep emitting
- %% events, either we stay busy or we drop out of flow control.
- case proplists:get_value(state, Infos) of
- flow -> ensure_stats_timer(State1);
- _ -> State1
- end.
-
-%% 1.0 stub
--ifdef(use_specs).
--spec(become_1_0/2 :: (non_neg_integer(), #v1{}) -> no_return()).
--endif.
-become_1_0(Id, State = #v1{sock = Sock}) ->
- case code:is_loaded(rabbit_amqp1_0_reader) of
- false -> refuse_connection(Sock, amqp1_0_plugin_not_enabled);
- _ -> Mode = case Id of
- 0 -> amqp;
- 3 -> sasl;
- _ -> refuse_connection(
- Sock, {unsupported_amqp1_0_protocol_id, Id},
- {3, 1, 0, 0})
- end,
- F = fun (_Deb, Buf, BufLen, S) ->
- {rabbit_amqp1_0_reader, init,
- [Mode, pack_for_1_0(Buf, BufLen, S)]}
- end,
- State#v1{connection_state = {become, F}}
- end.
-
-pack_for_1_0(Buf, BufLen, #v1{parent = Parent,
- sock = Sock,
- recv_len = RecvLen,
- pending_recv = PendingRecv,
- helper_sup = SupPid}) ->
- {Parent, Sock, RecvLen, PendingRecv, SupPid, Buf, BufLen}.
diff --git a/src/rabbit_runtime_parameter.erl b/src/rabbit_runtime_parameter.erl
deleted file mode 100644
index 1d4bc0b575..0000000000
--- a/src/rabbit_runtime_parameter.erl
+++ /dev/null
@@ -1,42 +0,0 @@
-%% The contents of this file are subject to the Mozilla Public License
-%% Version 1.1 (the "License"); you may not use this file except in
-%% compliance with the License. You may obtain a copy of the License
-%% at http://www.mozilla.org/MPL/
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and
-%% limitations under the License.
-%%
-%% The Original Code is RabbitMQ.
-%%
-%% The Initial Developer of the Original Code is GoPivotal, Inc.
-%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved.
-%%
-
--module(rabbit_runtime_parameter).
-
--ifdef(use_specs).
-
--type(validate_results() ::
- 'ok' | {error, string(), [term()]} | [validate_results()]).
-
--callback validate(rabbit_types:vhost(), binary(), binary(),
- term(), rabbit_types:user()) -> validate_results().
--callback notify(rabbit_types:vhost(), binary(), binary(), term()) -> 'ok'.
--callback notify_clear(rabbit_types:vhost(), binary(), binary()) -> 'ok'.
-
--else.
-
--export([behaviour_info/1]).
-
-behaviour_info(callbacks) ->
- [
- {validate, 5},
- {notify, 4},
- {notify_clear, 3}
- ];
-behaviour_info(_Other) ->
- undefined.
-
--endif.
diff --git a/src/rabbit_writer.erl b/src/rabbit_writer.erl
deleted file mode 100644
index 897d937443..0000000000
--- a/src/rabbit_writer.erl
+++ /dev/null
@@ -1,390 +0,0 @@
-%% The contents of this file are subject to the Mozilla Public License
-%% Version 1.1 (the "License"); you may not use this file except in
-%% compliance with the License. You may obtain a copy of the License
-%% at http://www.mozilla.org/MPL/
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and
-%% limitations under the License.
-%%
-%% The Original Code is RabbitMQ.
-%%
-%% The Initial Developer of the Original Code is GoPivotal, Inc.
-%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved.
-%%
-
--module(rabbit_writer).
-
-%% This module backs writer processes ("writers"). The responsibility of
-%% a writer is to serialise protocol methods and write them to the socket.
-%% Every writer is associated with a channel and normally it's the channel
-%% that delegates method delivery to it. However, rabbit_reader
-%% (connection process) can use this module's functions to send data
-%% on channel 0, which is only used for connection negotiation and
-%% other "special" purposes.
-%%
-%% This module provides multiple functions that send protocol commands,
-%% including some that are credit flow-aware.
-%%
-%% Writers perform internal buffering. When the amount of data
-%% buffered exceeds a threshold, a socket flush is performed.
-%% See FLUSH_THRESHOLD for details.
-%%
-%% When a socket write fails, writer will exit.
-
--include("rabbit.hrl").
--include("rabbit_framing.hrl").
-
--export([start/6, start_link/6, start/7, start_link/7]).
-
--export([system_continue/3, system_terminate/4, system_code_change/4]).
-
--export([send_command/2, send_command/3,
- send_command_sync/2, send_command_sync/3,
- send_command_and_notify/4, send_command_and_notify/5,
- send_command_flow/2, send_command_flow/3,
- flush/1]).
--export([internal_send_command/4, internal_send_command/6]).
-
-%% internal
--export([enter_mainloop/2, mainloop/2, mainloop1/2]).
-
--record(wstate, {
- %% socket (port)
- sock,
- %% channel number
- channel,
- %% connection-negotiated frame_max setting
- frame_max,
- %% see #connection.protocol in rabbit_reader
- protocol,
- %% connection (rabbit_reader) process
- reader,
- %% statistics emission timer
- stats_timer,
- %% data pending delivery (between socket
- %% flushes)
- pending
-}).
-
--define(HIBERNATE_AFTER, 5000).
-
-%%---------------------------------------------------------------------------
-
--ifdef(use_specs).
-
--spec(start/6 ::
- (rabbit_net:socket(), rabbit_channel:channel_number(),
- non_neg_integer(), rabbit_types:protocol(), pid(),
- rabbit_types:proc_name())
- -> rabbit_types:ok(pid())).
--spec(start_link/6 ::
- (rabbit_net:socket(), rabbit_channel:channel_number(),
- non_neg_integer(), rabbit_types:protocol(), pid(),
- rabbit_types:proc_name())
- -> rabbit_types:ok(pid())).
--spec(start/7 ::
- (rabbit_net:socket(), rabbit_channel:channel_number(),
- non_neg_integer(), rabbit_types:protocol(), pid(),
- rabbit_types:proc_name(), boolean())
- -> rabbit_types:ok(pid())).
--spec(start_link/7 ::
- (rabbit_net:socket(), rabbit_channel:channel_number(),
- non_neg_integer(), rabbit_types:protocol(), pid(),
- rabbit_types:proc_name(), boolean())
- -> rabbit_types:ok(pid())).
-
--spec(system_code_change/4 :: (_,_,_,_) -> {'ok',_}).
--spec(system_continue/3 :: (_,_,#wstate{}) -> any()).
--spec(system_terminate/4 :: (_,_,_,_) -> none()).
-
--spec(send_command/2 ::
- (pid(), rabbit_framing:amqp_method_record()) -> 'ok').
--spec(send_command/3 ::
- (pid(), rabbit_framing:amqp_method_record(), rabbit_types:content())
- -> 'ok').
--spec(send_command_sync/2 ::
- (pid(), rabbit_framing:amqp_method_record()) -> 'ok').
--spec(send_command_sync/3 ::
- (pid(), rabbit_framing:amqp_method_record(), rabbit_types:content())
- -> 'ok').
--spec(send_command_and_notify/4 ::
- (pid(), pid(), pid(), rabbit_framing:amqp_method_record())
- -> 'ok').
--spec(send_command_and_notify/5 ::
- (pid(), pid(), pid(), rabbit_framing:amqp_method_record(),
- rabbit_types:content())
- -> 'ok').
--spec(send_command_flow/2 ::
- (pid(), rabbit_framing:amqp_method_record()) -> 'ok').
--spec(send_command_flow/3 ::
- (pid(), rabbit_framing:amqp_method_record(), rabbit_types:content())
- -> 'ok').
--spec(flush/1 :: (pid()) -> 'ok').
--spec(internal_send_command/4 ::
- (rabbit_net:socket(), rabbit_channel:channel_number(),
- rabbit_framing:amqp_method_record(), rabbit_types:protocol())
- -> 'ok').
--spec(internal_send_command/6 ::
- (rabbit_net:socket(), rabbit_channel:channel_number(),
- rabbit_framing:amqp_method_record(), rabbit_types:content(),
- non_neg_integer(), rabbit_types:protocol())
- -> 'ok').
-
--endif.
-
-%%---------------------------------------------------------------------------
-
-start(Sock, Channel, FrameMax, Protocol, ReaderPid, Identity) ->
- start(Sock, Channel, FrameMax, Protocol, ReaderPid, Identity, false).
-
-start_link(Sock, Channel, FrameMax, Protocol, ReaderPid, Identity) ->
- start_link(Sock, Channel, FrameMax, Protocol, ReaderPid, Identity, false).
-
-start(Sock, Channel, FrameMax, Protocol, ReaderPid, Identity,
- ReaderWantsStats) ->
- State = initial_state(Sock, Channel, FrameMax, Protocol, ReaderPid,
- ReaderWantsStats),
- {ok, proc_lib:spawn(?MODULE, enter_mainloop, [Identity, State])}.
-
-start_link(Sock, Channel, FrameMax, Protocol, ReaderPid, Identity,
- ReaderWantsStats) ->
- State = initial_state(Sock, Channel, FrameMax, Protocol, ReaderPid,
- ReaderWantsStats),
- {ok, proc_lib:spawn_link(?MODULE, enter_mainloop, [Identity, State])}.
-
-initial_state(Sock, Channel, FrameMax, Protocol, ReaderPid, ReaderWantsStats) ->
- (case ReaderWantsStats of
- true -> fun rabbit_event:init_stats_timer/2;
- false -> fun rabbit_event:init_disabled_stats_timer/2
- end)(#wstate{sock = Sock,
- channel = Channel,
- frame_max = FrameMax,
- protocol = Protocol,
- reader = ReaderPid,
- pending = []},
- #wstate.stats_timer).
-
-system_continue(Parent, Deb, State) ->
- mainloop(Deb, State#wstate{reader = Parent}).
-
-system_terminate(Reason, _Parent, _Deb, _State) ->
- exit(Reason).
-
-system_code_change(Misc, _Module, _OldVsn, _Extra) ->
- {ok, Misc}.
-
-enter_mainloop(Identity, State) ->
- Deb = sys:debug_options([]),
- ?store_proc_name(Identity),
- mainloop(Deb, State).
-
-mainloop(Deb, State) ->
- try
- mainloop1(Deb, State)
- catch
- exit:Error -> #wstate{reader = ReaderPid, channel = Channel} = State,
- ReaderPid ! {channel_exit, Channel, Error}
- end,
- done.
-
-mainloop1(Deb, State = #wstate{pending = []}) ->
- receive
- Message -> {Deb1, State1} = handle_message(Deb, Message, State),
- ?MODULE:mainloop1(Deb1, State1)
- after ?HIBERNATE_AFTER ->
- erlang:hibernate(?MODULE, mainloop, [Deb, State])
- end;
-mainloop1(Deb, State) ->
- receive
- Message -> {Deb1, State1} = handle_message(Deb, Message, State),
- ?MODULE:mainloop1(Deb1, State1)
- after 0 ->
- ?MODULE:mainloop1(Deb, internal_flush(State))
- end.
-
-handle_message(Deb, {system, From, Req}, State = #wstate{reader = Parent}) ->
- sys:handle_system_msg(Req, From, Parent, ?MODULE, Deb, State);
-handle_message(Deb, Message, State) ->
- {Deb, handle_message(Message, State)}.
-
-handle_message({send_command, MethodRecord}, State) ->
- internal_send_command_async(MethodRecord, State);
-handle_message({send_command, MethodRecord, Content}, State) ->
- internal_send_command_async(MethodRecord, Content, State);
-handle_message({send_command_flow, MethodRecord, Sender}, State) ->
- credit_flow:ack(Sender),
- internal_send_command_async(MethodRecord, State);
-handle_message({send_command_flow, MethodRecord, Content, Sender}, State) ->
- credit_flow:ack(Sender),
- internal_send_command_async(MethodRecord, Content, State);
-handle_message({'$gen_call', From, {send_command_sync, MethodRecord}}, State) ->
- State1 = internal_flush(
- internal_send_command_async(MethodRecord, State)),
- gen_server:reply(From, ok),
- State1;
-handle_message({'$gen_call', From, {send_command_sync, MethodRecord, Content}},
- State) ->
- State1 = internal_flush(
- internal_send_command_async(MethodRecord, Content, State)),
- gen_server:reply(From, ok),
- State1;
-handle_message({'$gen_call', From, flush}, State) ->
- State1 = internal_flush(State),
- gen_server:reply(From, ok),
- State1;
-handle_message({send_command_and_notify, QPid, ChPid, MethodRecord}, State) ->
- State1 = internal_send_command_async(MethodRecord, State),
- rabbit_amqqueue:notify_sent(QPid, ChPid),
- State1;
-handle_message({send_command_and_notify, QPid, ChPid, MethodRecord, Content},
- State) ->
- State1 = internal_send_command_async(MethodRecord, Content, State),
- rabbit_amqqueue:notify_sent(QPid, ChPid),
- State1;
-handle_message({'DOWN', _MRef, process, QPid, _Reason}, State) ->
- rabbit_amqqueue:notify_sent_queue_down(QPid),
- State;
-handle_message({inet_reply, _, ok}, State) ->
- rabbit_event:ensure_stats_timer(State, #wstate.stats_timer, emit_stats);
-handle_message({inet_reply, _, Status}, _State) ->
- exit({writer, send_failed, Status});
-handle_message(emit_stats, State = #wstate{reader = ReaderPid}) ->
- ReaderPid ! ensure_stats,
- rabbit_event:reset_stats_timer(State, #wstate.stats_timer);
-handle_message(Message, _State) ->
- exit({writer, message_not_understood, Message}).
-
-%%---------------------------------------------------------------------------
-
-send_command(W, MethodRecord) ->
- W ! {send_command, MethodRecord},
- ok.
-
-send_command(W, MethodRecord, Content) ->
- W ! {send_command, MethodRecord, Content},
- ok.
-
-send_command_flow(W, MethodRecord) ->
- credit_flow:send(W),
- W ! {send_command_flow, MethodRecord, self()},
- ok.
-
-send_command_flow(W, MethodRecord, Content) ->
- credit_flow:send(W),
- W ! {send_command_flow, MethodRecord, Content, self()},
- ok.
-
-send_command_sync(W, MethodRecord) ->
- call(W, {send_command_sync, MethodRecord}).
-
-send_command_sync(W, MethodRecord, Content) ->
- call(W, {send_command_sync, MethodRecord, Content}).
-
-send_command_and_notify(W, Q, ChPid, MethodRecord) ->
- W ! {send_command_and_notify, Q, ChPid, MethodRecord},
- ok.
-
-send_command_and_notify(W, Q, ChPid, MethodRecord, Content) ->
- W ! {send_command_and_notify, Q, ChPid, MethodRecord, Content},
- ok.
-
-flush(W) -> call(W, flush).
-
-%%---------------------------------------------------------------------------
-
-call(Pid, Msg) ->
- {ok, Res} = gen:call(Pid, '$gen_call', Msg, infinity),
- Res.
-
-%%---------------------------------------------------------------------------
-
-assemble_frame(Channel, MethodRecord, Protocol) ->
- rabbit_binary_generator:build_simple_method_frame(
- Channel, MethodRecord, Protocol).
-
-assemble_frames(Channel, MethodRecord, Content, FrameMax, Protocol) ->
- MethodName = rabbit_misc:method_record_type(MethodRecord),
- true = Protocol:method_has_content(MethodName), % assertion
- MethodFrame = rabbit_binary_generator:build_simple_method_frame(
- Channel, MethodRecord, Protocol),
- ContentFrames = rabbit_binary_generator:build_simple_content_frames(
- Channel, Content, FrameMax, Protocol),
- [MethodFrame | ContentFrames].
-
-tcp_send(Sock, Data) ->
- rabbit_misc:throw_on_error(inet_error,
- fun () -> rabbit_net:send(Sock, Data) end).
-
-internal_send_command(Sock, Channel, MethodRecord, Protocol) ->
- ok = tcp_send(Sock, assemble_frame(Channel, MethodRecord, Protocol)).
-
-internal_send_command(Sock, Channel, MethodRecord, Content, FrameMax,
- Protocol) ->
- ok = lists:foldl(fun (Frame, ok) -> tcp_send(Sock, Frame);
- (_Frame, Other) -> Other
- end, ok, assemble_frames(Channel, MethodRecord,
- Content, FrameMax, Protocol)).
-
-internal_send_command_async(MethodRecord,
- State = #wstate{channel = Channel,
- protocol = Protocol,
- pending = Pending}) ->
- Frame = assemble_frame(Channel, MethodRecord, Protocol),
- maybe_flush(State#wstate{pending = [Frame | Pending]}).
-
-internal_send_command_async(MethodRecord, Content,
- State = #wstate{channel = Channel,
- frame_max = FrameMax,
- protocol = Protocol,
- pending = Pending}) ->
- Frames = assemble_frames(Channel, MethodRecord, Content, FrameMax,
- Protocol),
- rabbit_basic:maybe_gc_large_msg(Content),
- maybe_flush(State#wstate{pending = [Frames | Pending]}).
-
-%% When the amount of protocol method data buffered exceeds
-%% this threshold, a socket flush is performed.
-%%
-%% This magic number is the tcp-over-ethernet MSS (1460) minus the
-%% minimum size of a AMQP 0-9-1 basic.deliver method frame (24) plus basic
-%% content header (22). The idea is that we want to flush just before
-%% exceeding the MSS.
--define(FLUSH_THRESHOLD, 1414).
-
-maybe_flush(State = #wstate{pending = Pending}) ->
- case iolist_size(Pending) >= ?FLUSH_THRESHOLD of
- true -> internal_flush(State);
- false -> State
- end.
-
-internal_flush(State = #wstate{pending = []}) ->
- State;
-internal_flush(State = #wstate{sock = Sock, pending = Pending}) ->
- ok = port_cmd(Sock, lists:reverse(Pending)),
- State#wstate{pending = []}.
-
-%% gen_tcp:send/2 does a selective receive of {inet_reply, Sock,
-%% Status} to obtain the result. That is bad when it is called from
-%% the writer since it requires scanning of the writers possibly quite
-%% large message queue.
-%%
-%% So instead we lift the code from prim_inet:send/2, which is what
-%% gen_tcp:send/2 calls, do the first half here and then just process
-%% the result code in handle_message/2 as and when it arrives.
-%%
-%% This means we may end up happily sending data down a closed/broken
-%% socket, but that's ok since a) data in the buffers will be lost in
-%% any case (so qualitatively we are no worse off than if we used
-%% gen_tcp:send/2), and b) we do detect the changed socket status
-%% eventually, i.e. when we get round to handling the result code.
-%%
-%% Also note that the port has bounded buffers and port_command blocks
-%% when these are full. So the fact that we process the result
-%% asynchronously does not impact flow control.
-port_cmd(Sock, Data) ->
- true = try rabbit_net:port_command(Sock, Data)
- catch error:Error -> exit({writer, send_failed, Error})
- end,
- ok.
diff --git a/src/ssl_compat.erl b/src/ssl_compat.erl
deleted file mode 100644
index fc83fbcfa6..0000000000
--- a/src/ssl_compat.erl
+++ /dev/null
@@ -1,75 +0,0 @@
-%% The contents of this file are subject to the Mozilla Public License
-%% Version 1.1 (the "License"); you may not use this file except in
-%% compliance with the License. You may obtain a copy of the License
-%% at http://www.mozilla.org/MPL/
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and
-%% limitations under the License.
-%%
-%% The Original Code is RabbitMQ.
-%%
-%% The Initial Developer of the Original Code is GoPivotal, Inc.
-%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved.
-%%
-
--module(ssl_compat).
-
-%% We don't want warnings about the use of erlang:now/0 in
-%% this module.
--compile(nowarn_deprecated_function).
-
--export([connection_information/1,
- connection_information/2]).
-
-connection_information(SslSocket) ->
- try
- ssl:connection_information(SslSocket)
- catch
- error:undef ->
- case ssl:connection_info(SslSocket) of
- {ok, {ProtocolVersion, CipherSuite}} ->
- {ok, [{protocol, ProtocolVersion},
- {cipher_suite, CipherSuite}]};
- {error, Reason} ->
- {error, Reason}
- end
- end.
-
-connection_information(SslSocket, Items) ->
- try
- ssl:connection_information(SslSocket, Items)
- catch
- error:undef ->
- WantProtocolVersion = lists:member(protocol, Items),
- WantCipherSuite = lists:member(cipher_suite, Items),
- if
- WantProtocolVersion orelse WantCipherSuite ->
- case ssl:connection_info(SslSocket) of
- {ok, {ProtocolVersion, CipherSuite}} ->
- filter_information_items(ProtocolVersion,
- CipherSuite,
- Items,
- []);
- {error, Reason} ->
- {error, Reason}
- end;
- true ->
- {ok, []}
- end
- end.
-
-filter_information_items(ProtocolVersion, CipherSuite, [protocol | Rest],
- Result) ->
- filter_information_items(ProtocolVersion, CipherSuite, Rest,
- [{protocol, ProtocolVersion} | Result]);
-filter_information_items(ProtocolVersion, CipherSuite, [cipher_suite | Rest],
- Result) ->
- filter_information_items(ProtocolVersion, CipherSuite, Rest,
- [{cipher_suite, CipherSuite} | Result]);
-filter_information_items(ProtocolVersion, CipherSuite, [_ | Rest],
- Result) ->
- filter_information_items(ProtocolVersion, CipherSuite, Rest, Result);
-filter_information_items(_ProtocolVersion, _CipherSuite, [], Result) ->
- {ok, lists:reverse(Result)}.
diff --git a/src/supervisor2.erl b/src/supervisor2.erl
deleted file mode 100644
index c8ffbb12ea..0000000000
--- a/src/supervisor2.erl
+++ /dev/null
@@ -1,1553 +0,0 @@
-%% This file is a copy of supervisor.erl from the R16B Erlang/OTP
-%% distribution, with the following modifications:
-%%
-%% 1) the module name is supervisor2
-%%
-%% 2) a find_child/2 utility function has been added
-%%
-%% 3) Added an 'intrinsic' restart type. Like the transient type, this
-%% type means the child should only be restarted if the child exits
-%% abnormally. Unlike the transient type, if the child exits
-%% normally, the supervisor itself also exits normally. If the
-%% child is a supervisor and it exits normally (i.e. with reason of
-%% 'shutdown') then the child's parent also exits normally.
-%%
-%% 4) child specifications can contain, as the restart type, a tuple
-%% {permanent, Delay} | {transient, Delay} | {intrinsic, Delay}
-%% where Delay >= 0 (see point (4) below for intrinsic). The delay,
-%% in seconds, indicates what should happen if a child, upon being
-%% restarted, exceeds the MaxT and MaxR parameters. Thus, if a
-%% child exits, it is restarted as normal. If it exits sufficiently
-%% quickly and often to exceed the boundaries set by the MaxT and
-%% MaxR parameters, and a Delay is specified, then rather than
-%% stopping the supervisor, the supervisor instead continues and
-%% tries to start up the child again, Delay seconds later.
-%%
-%% Note that if a child is delay-restarted this will reset the
-%% count of restarts towrds MaxR and MaxT. This matters if MaxT >
-%% Delay, since otherwise we would fail to restart after the delay.
-%%
-%% Sometimes, you may wish for a transient or intrinsic child to
-%% exit abnormally so that it gets restarted, but still log
-%% nothing. gen_server will log any exit reason other than
-%% 'normal', 'shutdown' or {'shutdown', _}. Thus the exit reason of
-%% {'shutdown', 'restart'} is interpreted to mean you wish the
-%% child to be restarted according to the delay parameters, but
-%% gen_server will not log the error. Thus from gen_server's
-%% perspective it's a normal exit, whilst from supervisor's
-%% perspective, it's an abnormal exit.
-%%
-%% 5) normal, and {shutdown, _} exit reasons are all treated the same
-%% (i.e. are regarded as normal exits)
-%%
-%% All modifications are (C) 2010-2013 GoPivotal, Inc.
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1996-2012. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
--module(supervisor2).
-
--behaviour(gen_server).
-
-%% External exports
--export([start_link/2, start_link/3,
- start_child/2, restart_child/2,
- delete_child/2, terminate_child/2,
- which_children/1, count_children/1,
- find_child/2, check_childspecs/1]).
-
-%% Internal exports
--export([init/1, handle_call/3, handle_cast/2, handle_info/2,
- terminate/2, code_change/3]).
--export([try_again_restart/3]).
-
-%%--------------------------------------------------------------------------
--ifdef(use_specs).
--export_type([child_spec/0, startchild_ret/0, strategy/0, sup_name/0]).
--endif.
-%%--------------------------------------------------------------------------
-
--ifdef(use_specs).
--type child() :: 'undefined' | pid().
--type child_id() :: term().
--type mfargs() :: {M :: module(), F :: atom(), A :: [term()] | undefined}.
--type modules() :: [module()] | 'dynamic'.
--type delay() :: non_neg_integer().
--type restart() :: 'permanent' | 'transient' | 'temporary' | 'intrinsic' | {'permanent', delay()} | {'transient', delay()} | {'intrinsic', delay()}.
--type shutdown() :: 'brutal_kill' | timeout().
--type worker() :: 'worker' | 'supervisor'.
--type sup_name() :: {'local', Name :: atom()} | {'global', Name :: atom()}.
--type sup_ref() :: (Name :: atom())
- | {Name :: atom(), Node :: node()}
- | {'global', Name :: atom()}
- | pid().
--type child_spec() :: {Id :: child_id(),
- StartFunc :: mfargs(),
- Restart :: restart(),
- Shutdown :: shutdown(),
- Type :: worker(),
- Modules :: modules()}.
-
--type strategy() :: 'one_for_all' | 'one_for_one'
- | 'rest_for_one' | 'simple_one_for_one'.
--endif.
-
-%%--------------------------------------------------------------------------
-
--ifdef(use_specs).
--record(child, {% pid is undefined when child is not running
- pid = undefined :: child() | {restarting,pid()} | [pid()],
- name :: child_id(),
- mfargs :: mfargs(),
- restart_type :: restart(),
- shutdown :: shutdown(),
- child_type :: worker(),
- modules = [] :: modules()}).
--type child_rec() :: #child{}.
--else.
--record(child, {
- pid = undefined,
- name,
- mfargs,
- restart_type,
- shutdown,
- child_type,
- modules = []}).
--endif.
-
--define(DICT, dict).
--define(SETS, sets).
--define(SET, set).
-
--ifdef(use_specs).
--record(state, {name,
- strategy :: strategy(),
- children = [] :: [child_rec()],
- dynamics :: ?DICT:?DICT() | ?SETS:?SET(),
- intensity :: non_neg_integer(),
- period :: pos_integer(),
- restarts = [],
- module,
- args}).
--type state() :: #state{}.
--else.
--record(state, {name,
- strategy,
- children = [],
- dynamics,
- intensity,
- period,
- restarts = [],
- module,
- args}).
--endif.
-
--define(is_simple(State), State#state.strategy =:= simple_one_for_one).
--define(is_permanent(R), ((R =:= permanent) orelse
- (is_tuple(R) andalso
- tuple_size(R) == 2 andalso
- element(1, R) =:= permanent))).
--define(is_explicit_restart(R),
- R == {shutdown, restart}).
-
--ifdef(use_specs).
--callback init(Args :: term()) ->
- {ok, {{RestartStrategy :: strategy(),
- MaxR :: non_neg_integer(),
- MaxT :: non_neg_integer()},
- [ChildSpec :: child_spec()]}}
- | ignore.
--else.
-
--export([behaviour_info/1]).
-
-behaviour_info(callbacks) ->
- [{init,1}];
-behaviour_info(_Other) ->
- undefined.
-
--endif.
--define(restarting(_Pid_), {restarting,_Pid_}).
-
-%%% ---------------------------------------------------
-%%% This is a general process supervisor built upon gen_server.erl.
-%%% Servers/processes should/could also be built using gen_server.erl.
-%%% SupName = {local, atom()} | {global, atom()}.
-%%% ---------------------------------------------------
--ifdef(use_specs).
--type startlink_err() :: {'already_started', pid()}
- | {'shutdown', term()}
- | term().
--type startlink_ret() :: {'ok', pid()} | 'ignore' | {'error', startlink_err()}.
-
--spec start_link(Module, Args) -> startlink_ret() when
- Module :: module(),
- Args :: term().
-
--endif.
-start_link(Mod, Args) ->
- gen_server:start_link(?MODULE, {self, Mod, Args}, []).
-
--ifdef(use_specs).
--spec start_link(SupName, Module, Args) -> startlink_ret() when
- SupName :: sup_name(),
- Module :: module(),
- Args :: term().
--endif.
-start_link(SupName, Mod, Args) ->
- gen_server:start_link(SupName, ?MODULE, {SupName, Mod, Args}, []).
-
-%%% ---------------------------------------------------
-%%% Interface functions.
-%%% ---------------------------------------------------
--ifdef(use_specs).
--type startchild_err() :: 'already_present'
- | {'already_started', Child :: child()} | term().
--type startchild_ret() :: {'ok', Child :: child()}
- | {'ok', Child :: child(), Info :: term()}
- | {'error', startchild_err()}.
-
--spec start_child(SupRef, ChildSpec) -> startchild_ret() when
- SupRef :: sup_ref(),
- ChildSpec :: child_spec() | (List :: [term()]).
--endif.
-start_child(Supervisor, ChildSpec) ->
- call(Supervisor, {start_child, ChildSpec}).
-
--ifdef(use_specs).
--spec restart_child(SupRef, Id) -> Result when
- SupRef :: sup_ref(),
- Id :: child_id(),
- Result :: {'ok', Child :: child()}
- | {'ok', Child :: child(), Info :: term()}
- | {'error', Error},
- Error :: 'running' | 'restarting' | 'not_found' | 'simple_one_for_one' |
- term().
--endif.
-restart_child(Supervisor, Name) ->
- call(Supervisor, {restart_child, Name}).
-
--ifdef(use_specs).
--spec delete_child(SupRef, Id) -> Result when
- SupRef :: sup_ref(),
- Id :: child_id(),
- Result :: 'ok' | {'error', Error},
- Error :: 'running' | 'restarting' | 'not_found' | 'simple_one_for_one'.
--endif.
-delete_child(Supervisor, Name) ->
- call(Supervisor, {delete_child, Name}).
-
-%%-----------------------------------------------------------------
-%% Func: terminate_child/2
-%% Returns: ok | {error, Reason}
-%% Note that the child is *always* terminated in some
-%% way (maybe killed).
-%%-----------------------------------------------------------------
--ifdef(use_specs).
--spec terminate_child(SupRef, Id) -> Result when
- SupRef :: sup_ref(),
- Id :: pid() | child_id(),
- Result :: 'ok' | {'error', Error},
- Error :: 'not_found' | 'simple_one_for_one'.
--endif.
-terminate_child(Supervisor, Name) ->
- call(Supervisor, {terminate_child, Name}).
-
--ifdef(use_specs).
--spec which_children(SupRef) -> [{Id,Child,Type,Modules}] when
- SupRef :: sup_ref(),
- Id :: child_id() | undefined,
- Child :: child() | 'restarting',
- Type :: worker(),
- Modules :: modules().
--endif.
-which_children(Supervisor) ->
- call(Supervisor, which_children).
-
--ifdef(use_specs).
--spec count_children(SupRef) -> PropListOfCounts when
- SupRef :: sup_ref(),
- PropListOfCounts :: [Count],
- Count :: {specs, ChildSpecCount :: non_neg_integer()}
- | {active, ActiveProcessCount :: non_neg_integer()}
- | {supervisors, ChildSupervisorCount :: non_neg_integer()}
- |{workers, ChildWorkerCount :: non_neg_integer()}.
--endif.
-count_children(Supervisor) ->
- call(Supervisor, count_children).
-
--ifdef(use_specs).
--spec find_child(Supervisor, Name) -> [pid()] when
- Supervisor :: sup_ref(),
- Name :: child_id().
--endif.
-find_child(Supervisor, Name) ->
- [Pid || {Name1, Pid, _Type, _Modules} <- which_children(Supervisor),
- Name1 =:= Name].
-
-call(Supervisor, Req) ->
- gen_server:call(Supervisor, Req, infinity).
-
--ifdef(use_specs).
--spec check_childspecs(ChildSpecs) -> Result when
- ChildSpecs :: [child_spec()],
- Result :: 'ok' | {'error', Error :: term()}.
--endif.
-check_childspecs(ChildSpecs) when is_list(ChildSpecs) ->
- case check_startspec(ChildSpecs) of
- {ok, _} -> ok;
- Error -> {error, Error}
- end;
-check_childspecs(X) -> {error, {badarg, X}}.
-
-%%%-----------------------------------------------------------------
-%%% Called by timer:apply_after from restart/2
--ifdef(use_specs).
--spec try_again_restart(SupRef, Child, Reason) -> ok when
- SupRef :: sup_ref(),
- Child :: child_id() | pid(),
- Reason :: term().
--endif.
-try_again_restart(Supervisor, Child, Reason) ->
- cast(Supervisor, {try_again_restart, Child, Reason}).
-
-cast(Supervisor, Req) ->
- gen_server:cast(Supervisor, Req).
-
-%%% ---------------------------------------------------
-%%%
-%%% Initialize the supervisor.
-%%%
-%%% ---------------------------------------------------
--ifdef(use_specs).
--type init_sup_name() :: sup_name() | 'self'.
-
--type stop_rsn() :: {'shutdown', term()}
- | {'bad_return', {module(),'init', term()}}
- | {'bad_start_spec', term()}
- | {'start_spec', term()}
- | {'supervisor_data', term()}.
-
--spec init({init_sup_name(), module(), [term()]}) ->
- {'ok', state()} | 'ignore' | {'stop', stop_rsn()}.
--endif.
-init({SupName, Mod, Args}) ->
- process_flag(trap_exit, true),
- case Mod:init(Args) of
- {ok, {SupFlags, StartSpec}} ->
- case init_state(SupName, SupFlags, Mod, Args) of
- {ok, State} when ?is_simple(State) ->
- init_dynamic(State, StartSpec);
- {ok, State} ->
- init_children(State, StartSpec);
- Error ->
- {stop, {supervisor_data, Error}}
- end;
- ignore ->
- ignore;
- Error ->
- {stop, {bad_return, {Mod, init, Error}}}
- end.
-
-init_children(State, StartSpec) ->
- SupName = State#state.name,
- case check_startspec(StartSpec) of
- {ok, Children} ->
- case start_children(Children, SupName) of
- {ok, NChildren} ->
- {ok, State#state{children = NChildren}};
- {error, NChildren, Reason} ->
- terminate_children(NChildren, SupName),
- {stop, {shutdown, Reason}}
- end;
- Error ->
- {stop, {start_spec, Error}}
- end.
-
-init_dynamic(State, [StartSpec]) ->
- case check_startspec([StartSpec]) of
- {ok, Children} ->
- {ok, State#state{children = Children}};
- Error ->
- {stop, {start_spec, Error}}
- end;
-init_dynamic(_State, StartSpec) ->
- {stop, {bad_start_spec, StartSpec}}.
-
-%%-----------------------------------------------------------------
-%% Func: start_children/2
-%% Args: Children = [child_rec()] in start order
-%% SupName = {local, atom()} | {global, atom()} | {pid(), Mod}
-%% Purpose: Start all children. The new list contains #child's
-%% with pids.
-%% Returns: {ok, NChildren} | {error, NChildren, Reason}
-%% NChildren = [child_rec()] in termination order (reversed
-%% start order)
-%%-----------------------------------------------------------------
-start_children(Children, SupName) -> start_children(Children, [], SupName).
-
-start_children([Child|Chs], NChildren, SupName) ->
- case do_start_child(SupName, Child) of
- {ok, undefined} when Child#child.restart_type =:= temporary ->
- start_children(Chs, NChildren, SupName);
- {ok, Pid} ->
- start_children(Chs, [Child#child{pid = Pid}|NChildren], SupName);
- {ok, Pid, _Extra} ->
- start_children(Chs, [Child#child{pid = Pid}|NChildren], SupName);
- {error, Reason} ->
- report_error(start_error, Reason, Child, SupName),
- {error, lists:reverse(Chs) ++ [Child | NChildren],
- {failed_to_start_child,Child#child.name,Reason}}
- end;
-start_children([], NChildren, _SupName) ->
- {ok, NChildren}.
-
-do_start_child(SupName, Child) ->
- #child{mfargs = {M, F, Args}} = Child,
- case catch apply(M, F, Args) of
- {ok, Pid} when is_pid(Pid) ->
- NChild = Child#child{pid = Pid},
- report_progress(NChild, SupName),
- {ok, Pid};
- {ok, Pid, Extra} when is_pid(Pid) ->
- NChild = Child#child{pid = Pid},
- report_progress(NChild, SupName),
- {ok, Pid, Extra};
- ignore ->
- {ok, undefined};
- {error, What} -> {error, What};
- What -> {error, What}
- end.
-
-do_start_child_i(M, F, A) ->
- case catch apply(M, F, A) of
- {ok, Pid} when is_pid(Pid) ->
- {ok, Pid};
- {ok, Pid, Extra} when is_pid(Pid) ->
- {ok, Pid, Extra};
- ignore ->
- {ok, undefined};
- {error, Error} ->
- {error, Error};
- What ->
- {error, What}
- end.
-
-%%% ---------------------------------------------------
-%%%
-%%% Callback functions.
-%%%
-%%% ---------------------------------------------------
--ifdef(use_specs).
--type call() :: 'which_children' | 'count_children' | {_, _}. % XXX: refine
--spec handle_call(call(), term(), state()) -> {'reply', term(), state()}.
--endif.
-handle_call({start_child, EArgs}, _From, State) when ?is_simple(State) ->
- Child = hd(State#state.children),
- #child{mfargs = {M, F, A}} = Child,
- Args = A ++ EArgs,
- case do_start_child_i(M, F, Args) of
- {ok, undefined} when Child#child.restart_type =:= temporary ->
- {reply, {ok, undefined}, State};
- {ok, Pid} ->
- NState = save_dynamic_child(Child#child.restart_type, Pid, Args, State),
- {reply, {ok, Pid}, NState};
- {ok, Pid, Extra} ->
- NState = save_dynamic_child(Child#child.restart_type, Pid, Args, State),
- {reply, {ok, Pid, Extra}, NState};
- What ->
- {reply, What, State}
- end;
-
-%% terminate_child for simple_one_for_one can only be done with pid
-handle_call({terminate_child, Name}, _From, State) when not is_pid(Name),
- ?is_simple(State) ->
- {reply, {error, simple_one_for_one}, State};
-
-handle_call({terminate_child, Name}, _From, State) ->
- case get_child(Name, State, ?is_simple(State)) of
- {value, Child} ->
- case do_terminate(Child, State#state.name) of
- #child{restart_type=RT} when RT=:=temporary; ?is_simple(State) ->
- {reply, ok, state_del_child(Child, State)};
- NChild ->
- {reply, ok, replace_child(NChild, State)}
- end;
- false ->
- {reply, {error, not_found}, State}
- end;
-
-%%% The requests delete_child and restart_child are invalid for
-%%% simple_one_for_one supervisors.
-handle_call({_Req, _Data}, _From, State) when ?is_simple(State) ->
- {reply, {error, simple_one_for_one}, State};
-
-handle_call({start_child, ChildSpec}, _From, State) ->
- case check_childspec(ChildSpec) of
- {ok, Child} ->
- {Resp, NState} = handle_start_child(Child, State),
- {reply, Resp, NState};
- What ->
- {reply, {error, What}, State}
- end;
-
-handle_call({restart_child, Name}, _From, State) ->
- case get_child(Name, State) of
- {value, Child} when Child#child.pid =:= undefined ->
- case do_start_child(State#state.name, Child) of
- {ok, Pid} ->
- NState = replace_child(Child#child{pid = Pid}, State),
- {reply, {ok, Pid}, NState};
- {ok, Pid, Extra} ->
- NState = replace_child(Child#child{pid = Pid}, State),
- {reply, {ok, Pid, Extra}, NState};
- Error ->
- {reply, Error, State}
- end;
- {value, #child{pid=?restarting(_)}} ->
- {reply, {error, restarting}, State};
- {value, _} ->
- {reply, {error, running}, State};
- _ ->
- {reply, {error, not_found}, State}
- end;
-
-handle_call({delete_child, Name}, _From, State) ->
- case get_child(Name, State) of
- {value, Child} when Child#child.pid =:= undefined ->
- NState = remove_child(Child, State),
- {reply, ok, NState};
- {value, #child{pid=?restarting(_)}} ->
- {reply, {error, restarting}, State};
- {value, _} ->
- {reply, {error, running}, State};
- _ ->
- {reply, {error, not_found}, State}
- end;
-
-handle_call(which_children, _From, #state{children = [#child{restart_type = temporary,
- child_type = CT,
- modules = Mods}]} =
- State) when ?is_simple(State) ->
- Reply = lists:map(fun(Pid) -> {undefined, Pid, CT, Mods} end,
- ?SETS:to_list(dynamics_db(temporary, State#state.dynamics))),
- {reply, Reply, State};
-
-handle_call(which_children, _From, #state{children = [#child{restart_type = RType,
- child_type = CT,
- modules = Mods}]} =
- State) when ?is_simple(State) ->
- Reply = lists:map(fun({?restarting(_),_}) -> {undefined,restarting,CT,Mods};
- ({Pid, _}) -> {undefined, Pid, CT, Mods} end,
- ?DICT:to_list(dynamics_db(RType, State#state.dynamics))),
- {reply, Reply, State};
-
-handle_call(which_children, _From, State) ->
- Resp =
- lists:map(fun(#child{pid = ?restarting(_), name = Name,
- child_type = ChildType, modules = Mods}) ->
- {Name, restarting, ChildType, Mods};
- (#child{pid = Pid, name = Name,
- child_type = ChildType, modules = Mods}) ->
- {Name, Pid, ChildType, Mods}
- end,
- State#state.children),
- {reply, Resp, State};
-
-
-handle_call(count_children, _From, #state{children = [#child{restart_type = temporary,
- child_type = CT}]} = State)
- when ?is_simple(State) ->
- {Active, Count} =
- ?SETS:fold(fun(Pid, {Alive, Tot}) ->
- case is_pid(Pid) andalso is_process_alive(Pid) of
- true ->{Alive+1, Tot +1};
- false ->
- {Alive, Tot + 1}
- end
- end, {0, 0}, dynamics_db(temporary, State#state.dynamics)),
- Reply = case CT of
- supervisor -> [{specs, 1}, {active, Active},
- {supervisors, Count}, {workers, 0}];
- worker -> [{specs, 1}, {active, Active},
- {supervisors, 0}, {workers, Count}]
- end,
- {reply, Reply, State};
-
-handle_call(count_children, _From, #state{children = [#child{restart_type = RType,
- child_type = CT}]} = State)
- when ?is_simple(State) ->
- {Active, Count} =
- ?DICT:fold(fun(Pid, _Val, {Alive, Tot}) ->
- case is_pid(Pid) andalso is_process_alive(Pid) of
- true ->
- {Alive+1, Tot +1};
- false ->
- {Alive, Tot + 1}
- end
- end, {0, 0}, dynamics_db(RType, State#state.dynamics)),
- Reply = case CT of
- supervisor -> [{specs, 1}, {active, Active},
- {supervisors, Count}, {workers, 0}];
- worker -> [{specs, 1}, {active, Active},
- {supervisors, 0}, {workers, Count}]
- end,
- {reply, Reply, State};
-
-handle_call(count_children, _From, State) ->
- %% Specs and children are together on the children list...
- {Specs, Active, Supers, Workers} =
- lists:foldl(fun(Child, Counts) ->
- count_child(Child, Counts)
- end, {0,0,0,0}, State#state.children),
-
- %% Reformat counts to a property list.
- Reply = [{specs, Specs}, {active, Active},
- {supervisors, Supers}, {workers, Workers}],
- {reply, Reply, State}.
-
-
-count_child(#child{pid = Pid, child_type = worker},
- {Specs, Active, Supers, Workers}) ->
- case is_pid(Pid) andalso is_process_alive(Pid) of
- true -> {Specs+1, Active+1, Supers, Workers+1};
- false -> {Specs+1, Active, Supers, Workers+1}
- end;
-count_child(#child{pid = Pid, child_type = supervisor},
- {Specs, Active, Supers, Workers}) ->
- case is_pid(Pid) andalso is_process_alive(Pid) of
- true -> {Specs+1, Active+1, Supers+1, Workers};
- false -> {Specs+1, Active, Supers+1, Workers}
- end.
-
-
-%%% If a restart attempt failed, this message is sent via
-%%% timer:apply_after(0,...) in order to give gen_server the chance to
-%%% check it's inbox before trying again.
--ifdef(use_specs).
--spec handle_cast({try_again_restart, child_id() | pid(), term()}, state()) ->
- {'noreply', state()} | {stop, shutdown, state()}.
--endif.
-handle_cast({try_again_restart,Pid,Reason}, #state{children=[Child]}=State)
- when ?is_simple(State) ->
- RT = Child#child.restart_type,
- RPid = restarting(Pid),
- case dynamic_child_args(RPid, dynamics_db(RT, State#state.dynamics)) of
- {ok, Args} ->
- {M, F, _} = Child#child.mfargs,
- NChild = Child#child{pid = RPid, mfargs = {M, F, Args}},
- try_restart(Child#child.restart_type, Reason, NChild, State);
- error ->
- {noreply, State}
- end;
-
-handle_cast({try_again_restart,Name,Reason}, State) ->
- %% we still support >= R12-B3 in which lists:keyfind/3 doesn't exist
- case lists:keysearch(Name,#child.name,State#state.children) of
- {value, Child = #child{pid=?restarting(_), restart_type=RestartType}} ->
- try_restart(RestartType, Reason, Child, State);
- _ ->
- {noreply,State}
- end.
-
-%%
-%% Take care of terminated children.
-%%
--ifdef(use_specs).
--spec handle_info(term(), state()) ->
- {'noreply', state()} | {'stop', 'shutdown', state()}.
--endif.
-handle_info({'EXIT', Pid, Reason}, State) ->
- case restart_child(Pid, Reason, State) of
- {ok, State1} ->
- {noreply, State1};
- {shutdown, State1} ->
- {stop, shutdown, State1}
- end;
-
-handle_info({delayed_restart, {RestartType, Reason, Child}}, State)
- when ?is_simple(State) ->
- try_restart(RestartType, Reason, Child, State#state{restarts = []}); %% [1]
-handle_info({delayed_restart, {RestartType, Reason, Child}}, State) ->
- case get_child(Child#child.name, State) of
- {value, Child1} ->
- try_restart(RestartType, Reason, Child1,
- State#state{restarts = []}); %% [1]
- _What ->
- {noreply, State}
- end;
-%% [1] When we receive a delayed_restart message we want to reset the
-%% restarts field since otherwise the MaxT might not have elapsed and
-%% we would just delay again and again. Since a common use of the
-%% delayed restart feature is for MaxR = 1, MaxT = some huge number
-%% (so that we don't end up bouncing around in non-delayed restarts)
-%% this is important.
-
-handle_info(Msg, State) ->
- error_logger:error_msg("Supervisor received unexpected message: ~p~n",
- [Msg]),
- {noreply, State}.
-
-%%
-%% Terminate this server.
-%%
--ifdef(use_specs).
--spec terminate(term(), state()) -> 'ok'.
--endif.
-terminate(_Reason, #state{children=[Child]} = State) when ?is_simple(State) ->
- terminate_dynamic_children(Child, dynamics_db(Child#child.restart_type,
- State#state.dynamics),
- State#state.name);
-terminate(_Reason, State) ->
- terminate_children(State#state.children, State#state.name).
-
-%%
-%% Change code for the supervisor.
-%% Call the new call-back module and fetch the new start specification.
-%% Combine the new spec. with the old. If the new start spec. is
-%% not valid the code change will not succeed.
-%% Use the old Args as argument to Module:init/1.
-%% NOTE: This requires that the init function of the call-back module
-%% does not have any side effects.
-%%
--ifdef(use_specs).
--spec code_change(term(), state(), term()) ->
- {'ok', state()} | {'error', term()}.
--endif.
-code_change(_, State, _) ->
- case (State#state.module):init(State#state.args) of
- {ok, {SupFlags, StartSpec}} ->
- case catch check_flags(SupFlags) of
- ok ->
- {Strategy, MaxIntensity, Period} = SupFlags,
- update_childspec(State#state{strategy = Strategy,
- intensity = MaxIntensity,
- period = Period},
- StartSpec);
- Error ->
- {error, Error}
- end;
- ignore ->
- {ok, State};
- Error ->
- Error
- end.
-
-check_flags({Strategy, MaxIntensity, Period}) ->
- validStrategy(Strategy),
- validIntensity(MaxIntensity),
- validPeriod(Period),
- ok;
-check_flags(What) ->
- {bad_flags, What}.
-
-update_childspec(State, StartSpec) when ?is_simple(State) ->
- case check_startspec(StartSpec) of
- {ok, [Child]} ->
- {ok, State#state{children = [Child]}};
- Error ->
- {error, Error}
- end;
-update_childspec(State, StartSpec) ->
- case check_startspec(StartSpec) of
- {ok, Children} ->
- OldC = State#state.children, % In reverse start order !
- NewC = update_childspec1(OldC, Children, []),
- {ok, State#state{children = NewC}};
- Error ->
- {error, Error}
- end.
-
-update_childspec1([Child|OldC], Children, KeepOld) ->
- case update_chsp(Child, Children) of
- {ok,NewChildren} ->
- update_childspec1(OldC, NewChildren, KeepOld);
- false ->
- update_childspec1(OldC, Children, [Child|KeepOld])
- end;
-update_childspec1([], Children, KeepOld) ->
- %% Return them in (kept) reverse start order.
- lists:reverse(Children ++ KeepOld).
-
-update_chsp(OldCh, Children) ->
- case lists:map(fun(Ch) when OldCh#child.name =:= Ch#child.name ->
- Ch#child{pid = OldCh#child.pid};
- (Ch) ->
- Ch
- end,
- Children) of
- Children ->
- false; % OldCh not found in new spec.
- NewC ->
- {ok, NewC}
- end.
-
-%%% ---------------------------------------------------
-%%% Start a new child.
-%%% ---------------------------------------------------
-
-handle_start_child(Child, State) ->
- case get_child(Child#child.name, State) of
- false ->
- case do_start_child(State#state.name, Child) of
- {ok, undefined} when Child#child.restart_type =:= temporary ->
- {{ok, undefined}, State};
- {ok, Pid} ->
- {{ok, Pid}, save_child(Child#child{pid = Pid}, State)};
- {ok, Pid, Extra} ->
- {{ok, Pid, Extra}, save_child(Child#child{pid = Pid}, State)};
- {error, What} ->
- {{error, {What, Child}}, State}
- end;
- {value, OldChild} when is_pid(OldChild#child.pid) ->
- {{error, {already_started, OldChild#child.pid}}, State};
- {value, _OldChild} ->
- {{error, already_present}, State}
- end.
-
-%%% ---------------------------------------------------
-%%% Restart. A process has terminated.
-%%% Returns: {ok, state()} | {shutdown, state()}
-%%% ---------------------------------------------------
-
-restart_child(Pid, Reason, #state{children = [Child]} = State) when ?is_simple(State) ->
- RestartType = Child#child.restart_type,
- case dynamic_child_args(Pid, dynamics_db(RestartType, State#state.dynamics)) of
- {ok, Args} ->
- {M, F, _} = Child#child.mfargs,
- NChild = Child#child{pid = Pid, mfargs = {M, F, Args}},
- do_restart(RestartType, Reason, NChild, State);
- error ->
- {ok, State}
- end;
-
-restart_child(Pid, Reason, State) ->
- Children = State#state.children,
- %% we still support >= R12-B3 in which lists:keyfind/3 doesn't exist
- case lists:keysearch(Pid, #child.pid, Children) of
- {value, #child{restart_type = RestartType} = Child} ->
- do_restart(RestartType, Reason, Child, State);
- false ->
- {ok, State}
- end.
-
-try_restart(RestartType, Reason, Child, State) ->
- case handle_restart(RestartType, Reason, Child, State) of
- {ok, NState} -> {noreply, NState};
- {shutdown, State2} -> {stop, shutdown, State2}
- end.
-
-do_restart(RestartType, Reason, Child, State) ->
- maybe_report_error(RestartType, Reason, Child, State),
- handle_restart(RestartType, Reason, Child, State).
-
-maybe_report_error(permanent, Reason, Child, State) ->
- report_child_termination(Reason, Child, State);
-maybe_report_error({permanent, _}, Reason, Child, State) ->
- report_child_termination(Reason, Child, State);
-maybe_report_error(_Type, Reason, Child, State) ->
- case is_abnormal_termination(Reason) of
- true -> report_child_termination(Reason, Child, State);
- false -> ok
- end.
-
-report_child_termination(Reason, Child, State) ->
- report_error(child_terminated, Reason, Child, State#state.name).
-
-handle_restart(permanent, _Reason, Child, State) ->
- restart(Child, State);
-handle_restart(transient, Reason, Child, State) ->
- restart_if_explicit_or_abnormal(fun restart/2,
- fun delete_child_and_continue/2,
- Reason, Child, State);
-handle_restart(intrinsic, Reason, Child, State) ->
- restart_if_explicit_or_abnormal(fun restart/2,
- fun delete_child_and_stop/2,
- Reason, Child, State);
-handle_restart(temporary, _Reason, Child, State) ->
- delete_child_and_continue(Child, State);
-handle_restart({permanent, _Delay}=Restart, Reason, Child, State) ->
- do_restart_delay(Restart, Reason, Child, State);
-handle_restart({transient, _Delay}=Restart, Reason, Child, State) ->
- restart_if_explicit_or_abnormal(defer_to_restart_delay(Restart, Reason),
- fun delete_child_and_continue/2,
- Reason, Child, State);
-handle_restart({intrinsic, _Delay}=Restart, Reason, Child, State) ->
- restart_if_explicit_or_abnormal(defer_to_restart_delay(Restart, Reason),
- fun delete_child_and_stop/2,
- Reason, Child, State).
-
-restart_if_explicit_or_abnormal(RestartHow, Otherwise, Reason, Child, State) ->
- case ?is_explicit_restart(Reason) orelse is_abnormal_termination(Reason) of
- true -> RestartHow(Child, State);
- false -> Otherwise(Child, State)
- end.
-
-defer_to_restart_delay(Restart, Reason) ->
- fun(Child, State) -> do_restart_delay(Restart, Reason, Child, State) end.
-
-delete_child_and_continue(Child, State) ->
- {ok, state_del_child(Child, State)}.
-
-delete_child_and_stop(Child, State) ->
- {shutdown, state_del_child(Child, State)}.
-
-is_abnormal_termination(normal) -> false;
-is_abnormal_termination(shutdown) -> false;
-is_abnormal_termination({shutdown, _}) -> false;
-is_abnormal_termination(_Other) -> true.
-
-do_restart_delay({RestartType, Delay}, Reason, Child, State) ->
- case add_restart(State) of
- {ok, NState} ->
- maybe_restart(NState#state.strategy, Child, NState);
- {terminate, _NState} ->
- %% we've reached the max restart intensity, but the
- %% add_restart will have added to the restarts
- %% field. Given we don't want to die here, we need to go
- %% back to the old restarts field otherwise we'll never
- %% attempt to restart later, which is why we ignore
- %% NState for this clause.
- _TRef = erlang:send_after(trunc(Delay*1000), self(),
- {delayed_restart,
- {{RestartType, Delay}, Reason, Child}}),
- {ok, state_del_child(Child, State)}
- end.
-
-restart(Child, State) ->
- case add_restart(State) of
- {ok, NState} ->
- maybe_restart(NState#state.strategy, Child, NState);
- {terminate, NState} ->
- report_error(shutdown, reached_max_restart_intensity,
- Child, State#state.name),
- {shutdown, remove_child(Child, NState)}
- end.
-
-maybe_restart(Strategy, Child, State) ->
- case restart(Strategy, Child, State) of
- {try_again, Reason, NState2} ->
- %% Leaving control back to gen_server before
- %% trying again. This way other incoming requsts
- %% for the supervisor can be handled - e.g. a
- %% shutdown request for the supervisor or the
- %% child.
- Id = if ?is_simple(State) -> Child#child.pid;
- true -> Child#child.name
- end,
- timer:apply_after(0,?MODULE,try_again_restart,[self(),Id,Reason]),
- {ok,NState2};
- Other ->
- Other
- end.
-
-restart(simple_one_for_one, Child, State) ->
- #child{pid = OldPid, mfargs = {M, F, A}} = Child,
- Dynamics = ?DICT:erase(OldPid, dynamics_db(Child#child.restart_type,
- State#state.dynamics)),
- case do_start_child_i(M, F, A) of
- {ok, Pid} ->
- NState = State#state{dynamics = ?DICT:store(Pid, A, Dynamics)},
- {ok, NState};
- {ok, Pid, _Extra} ->
- NState = State#state{dynamics = ?DICT:store(Pid, A, Dynamics)},
- {ok, NState};
- {error, Error} ->
- NState = State#state{dynamics = ?DICT:store(restarting(OldPid), A,
- Dynamics)},
- report_error(start_error, Error, Child, State#state.name),
- {try_again, Error, NState}
- end;
-restart(one_for_one, Child, State) ->
- OldPid = Child#child.pid,
- case do_start_child(State#state.name, Child) of
- {ok, Pid} ->
- NState = replace_child(Child#child{pid = Pid}, State),
- {ok, NState};
- {ok, Pid, _Extra} ->
- NState = replace_child(Child#child{pid = Pid}, State),
- {ok, NState};
- {error, Reason} ->
- NState = replace_child(Child#child{pid = restarting(OldPid)}, State),
- report_error(start_error, Reason, Child, State#state.name),
- {try_again, Reason, NState}
- end;
-restart(rest_for_one, Child, State) ->
- {ChAfter, ChBefore} = split_child(Child#child.pid, State#state.children),
- ChAfter2 = terminate_children(ChAfter, State#state.name),
- case start_children(ChAfter2, State#state.name) of
- {ok, ChAfter3} ->
- {ok, State#state{children = ChAfter3 ++ ChBefore}};
- {error, ChAfter3, Reason} ->
- NChild = Child#child{pid=restarting(Child#child.pid)},
- NState = State#state{children = ChAfter3 ++ ChBefore},
- {try_again, Reason, replace_child(NChild,NState)}
- end;
-restart(one_for_all, Child, State) ->
- Children1 = del_child(Child#child.pid, State#state.children),
- Children2 = terminate_children(Children1, State#state.name),
- case start_children(Children2, State#state.name) of
- {ok, NChs} ->
- {ok, State#state{children = NChs}};
- {error, NChs, Reason} ->
- NChild = Child#child{pid=restarting(Child#child.pid)},
- NState = State#state{children = NChs},
- {try_again, Reason, replace_child(NChild,NState)}
- end.
-
-restarting(Pid) when is_pid(Pid) -> ?restarting(Pid);
-restarting(RPid) -> RPid.
-
-%%-----------------------------------------------------------------
-%% Func: terminate_children/2
-%% Args: Children = [child_rec()] in termination order
-%% SupName = {local, atom()} | {global, atom()} | {pid(),Mod}
-%% Returns: NChildren = [child_rec()] in
-%% startup order (reversed termination order)
-%%-----------------------------------------------------------------
-terminate_children(Children, SupName) ->
- terminate_children(Children, SupName, []).
-
-%% Temporary children should not be restarted and thus should
-%% be skipped when building the list of terminated children, although
-%% we do want them to be shut down as many functions from this module
-%% use this function to just clear everything.
-terminate_children([Child = #child{restart_type=temporary} | Children], SupName, Res) ->
- do_terminate(Child, SupName),
- terminate_children(Children, SupName, Res);
-terminate_children([Child | Children], SupName, Res) ->
- NChild = do_terminate(Child, SupName),
- terminate_children(Children, SupName, [NChild | Res]);
-terminate_children([], _SupName, Res) ->
- Res.
-
-do_terminate(Child, SupName) when is_pid(Child#child.pid) ->
- case shutdown(Child#child.pid, Child#child.shutdown) of
- ok ->
- ok;
- {error, normal} when not ?is_permanent(Child#child.restart_type) ->
- ok;
- {error, OtherReason} ->
- report_error(shutdown_error, OtherReason, Child, SupName)
- end,
- Child#child{pid = undefined};
-do_terminate(Child, _SupName) ->
- Child#child{pid = undefined}.
-
-%%-----------------------------------------------------------------
-%% Shutdowns a child. We must check the EXIT value
-%% of the child, because it might have died with another reason than
-%% the wanted. In that case we want to report the error. We put a
-%% monitor on the child an check for the 'DOWN' message instead of
-%% checking for the 'EXIT' message, because if we check the 'EXIT'
-%% message a "naughty" child, who does unlink(Sup), could hang the
-%% supervisor.
-%% Returns: ok | {error, OtherReason} (this should be reported)
-%%-----------------------------------------------------------------
-shutdown(Pid, brutal_kill) ->
- case monitor_child(Pid) of
- ok ->
- exit(Pid, kill),
- receive
- {'DOWN', _MRef, process, Pid, killed} ->
- ok;
- {'DOWN', _MRef, process, Pid, OtherReason} ->
- {error, OtherReason}
- end;
- {error, Reason} ->
- {error, Reason}
- end;
-shutdown(Pid, Time) ->
- case monitor_child(Pid) of
- ok ->
- exit(Pid, shutdown), %% Try to shutdown gracefully
- receive
- {'DOWN', _MRef, process, Pid, shutdown} ->
- ok;
- {'DOWN', _MRef, process, Pid, OtherReason} ->
- {error, OtherReason}
- after Time ->
- exit(Pid, kill), %% Force termination.
- receive
- {'DOWN', _MRef, process, Pid, OtherReason} ->
- {error, OtherReason}
- end
- end;
- {error, Reason} ->
- {error, Reason}
- end.
-
-%% Help function to shutdown/2 switches from link to monitor approach
-monitor_child(Pid) ->
-
- %% Do the monitor operation first so that if the child dies
- %% before the monitoring is done causing a 'DOWN'-message with
- %% reason noproc, we will get the real reason in the 'EXIT'-message
- %% unless a naughty child has already done unlink...
- erlang:monitor(process, Pid),
- unlink(Pid),
-
- receive
- %% If the child dies before the unlik we must empty
- %% the mail-box of the 'EXIT'-message and the 'DOWN'-message.
- {'EXIT', Pid, Reason} ->
- receive
- {'DOWN', _, process, Pid, _} ->
- {error, Reason}
- end
- after 0 ->
- %% If a naughty child did unlink and the child dies before
- %% monitor the result will be that shutdown/2 receives a
- %% 'DOWN'-message with reason noproc.
- %% If the child should die after the unlink there
- %% will be a 'DOWN'-message with a correct reason
- %% that will be handled in shutdown/2.
- ok
- end.
-
-
-%%-----------------------------------------------------------------
-%% Func: terminate_dynamic_children/3
-%% Args: Child = child_rec()
-%% Dynamics = ?DICT() | ?SET()
-%% SupName = {local, atom()} | {global, atom()} | {pid(),Mod}
-%% Returns: ok
-%%
-%%
-%% Shutdown all dynamic children. This happens when the supervisor is
-%% stopped. Because the supervisor can have millions of dynamic children, we
-%% can have an significative overhead here.
-%%-----------------------------------------------------------------
-terminate_dynamic_children(Child, Dynamics, SupName) ->
- {Pids, EStack0} = monitor_dynamic_children(Child, Dynamics),
- Sz = ?SETS:size(Pids),
- EStack = case Child#child.shutdown of
- brutal_kill ->
- ?SETS:fold(fun(P, _) -> exit(P, kill) end, ok, Pids),
- wait_dynamic_children(Child, Pids, Sz, undefined, EStack0);
- infinity ->
- ?SETS:fold(fun(P, _) -> exit(P, shutdown) end, ok, Pids),
- wait_dynamic_children(Child, Pids, Sz, undefined, EStack0);
- Time ->
- ?SETS:fold(fun(P, _) -> exit(P, shutdown) end, ok, Pids),
- TRef = erlang:start_timer(Time, self(), kill),
- wait_dynamic_children(Child, Pids, Sz, TRef, EStack0)
- end,
- %% Unroll stacked errors and report them
- ?DICT:fold(fun(Reason, Ls, _) ->
- report_error(shutdown_error, Reason,
- Child#child{pid=Ls}, SupName)
- end, ok, EStack).
-
-
-monitor_dynamic_children(#child{restart_type=temporary}, Dynamics) ->
- ?SETS:fold(fun(P, {Pids, EStack}) ->
- case monitor_child(P) of
- ok ->
- {?SETS:add_element(P, Pids), EStack};
- {error, normal} ->
- {Pids, EStack};
- {error, Reason} ->
- {Pids, ?DICT:append(Reason, P, EStack)}
- end
- end, {?SETS:new(), ?DICT:new()}, Dynamics);
-monitor_dynamic_children(#child{restart_type=RType}, Dynamics) ->
- ?DICT:fold(fun(P, _, {Pids, EStack}) when is_pid(P) ->
- case monitor_child(P) of
- ok ->
- {?SETS:add_element(P, Pids), EStack};
- {error, normal} when not ?is_permanent(RType) ->
- {Pids, EStack};
- {error, Reason} ->
- {Pids, ?DICT:append(Reason, P, EStack)}
- end;
- (?restarting(_), _, {Pids, EStack}) ->
- {Pids, EStack}
- end, {?SETS:new(), ?DICT:new()}, Dynamics).
-
-wait_dynamic_children(_Child, _Pids, 0, undefined, EStack) ->
- EStack;
-wait_dynamic_children(_Child, _Pids, 0, TRef, EStack) ->
- %% If the timer has expired before its cancellation, we must empty the
- %% mail-box of the 'timeout'-message.
- erlang:cancel_timer(TRef),
- receive
- {timeout, TRef, kill} ->
- EStack
- after 0 ->
- EStack
- end;
-wait_dynamic_children(#child{shutdown=brutal_kill} = Child, Pids, Sz,
- TRef, EStack) ->
- receive
- {'DOWN', _MRef, process, Pid, killed} ->
- wait_dynamic_children(Child, ?SETS:del_element(Pid, Pids), Sz-1,
- TRef, EStack);
-
- {'DOWN', _MRef, process, Pid, Reason} ->
- wait_dynamic_children(Child, ?SETS:del_element(Pid, Pids), Sz-1,
- TRef, ?DICT:append(Reason, Pid, EStack))
- end;
-wait_dynamic_children(#child{restart_type=RType} = Child, Pids, Sz,
- TRef, EStack) ->
- receive
- {'DOWN', _MRef, process, Pid, shutdown} ->
- wait_dynamic_children(Child, ?SETS:del_element(Pid, Pids), Sz-1,
- TRef, EStack);
-
- {'DOWN', _MRef, process, Pid, normal} when not ?is_permanent(RType) ->
- wait_dynamic_children(Child, ?SETS:del_element(Pid, Pids), Sz-1,
- TRef, EStack);
-
- {'DOWN', _MRef, process, Pid, Reason} ->
- wait_dynamic_children(Child, ?SETS:del_element(Pid, Pids), Sz-1,
- TRef, ?DICT:append(Reason, Pid, EStack));
-
- {timeout, TRef, kill} ->
- ?SETS:fold(fun(P, _) -> exit(P, kill) end, ok, Pids),
- wait_dynamic_children(Child, Pids, Sz-1, undefined, EStack)
- end.
-
-%%-----------------------------------------------------------------
-%% Child/State manipulating functions.
-%%-----------------------------------------------------------------
-
-%% Note we do not want to save the parameter list for temporary processes as
-%% they will not be restarted, and hence we do not need this information.
-%% Especially for dynamic children to simple_one_for_one supervisors
-%% it could become very costly as it is not uncommon to spawn
-%% very many such processes.
-save_child(#child{restart_type = temporary,
- mfargs = {M, F, _}} = Child, #state{children = Children} = State) ->
- State#state{children = [Child#child{mfargs = {M, F, undefined}} |Children]};
-save_child(Child, #state{children = Children} = State) ->
- State#state{children = [Child |Children]}.
-
-save_dynamic_child(temporary, Pid, _, #state{dynamics = Dynamics} = State) ->
- State#state{dynamics = ?SETS:add_element(Pid, dynamics_db(temporary, Dynamics))};
-save_dynamic_child(RestartType, Pid, Args, #state{dynamics = Dynamics} = State) ->
- State#state{dynamics = ?DICT:store(Pid, Args, dynamics_db(RestartType, Dynamics))}.
-
-dynamics_db(temporary, undefined) ->
- ?SETS:new();
-dynamics_db(_, undefined) ->
- ?DICT:new();
-dynamics_db(_,Dynamics) ->
- Dynamics.
-
-dynamic_child_args(Pid, Dynamics) ->
- case ?SETS:is_set(Dynamics) of
- true ->
- {ok, undefined};
- false ->
- ?DICT:find(Pid, Dynamics)
- end.
-
-state_del_child(#child{pid = Pid, restart_type = temporary}, State) when ?is_simple(State) ->
- NDynamics = ?SETS:del_element(Pid, dynamics_db(temporary, State#state.dynamics)),
- State#state{dynamics = NDynamics};
-state_del_child(#child{pid = Pid, restart_type = RType}, State) when ?is_simple(State) ->
- NDynamics = ?DICT:erase(Pid, dynamics_db(RType, State#state.dynamics)),
- State#state{dynamics = NDynamics};
-state_del_child(Child, State) ->
- NChildren = del_child(Child#child.name, State#state.children),
- State#state{children = NChildren}.
-
-del_child(Name, [Ch=#child{pid = ?restarting(_)}|_]=Chs) when Ch#child.name =:= Name ->
- Chs;
-del_child(Name, [Ch|Chs]) when Ch#child.name =:= Name, Ch#child.restart_type =:= temporary ->
- Chs;
-del_child(Name, [Ch|Chs]) when Ch#child.name =:= Name ->
- [Ch#child{pid = undefined} | Chs];
-del_child(Pid, [Ch|Chs]) when Ch#child.pid =:= Pid, Ch#child.restart_type =:= temporary ->
- Chs;
-del_child(Pid, [Ch|Chs]) when Ch#child.pid =:= Pid ->
- [Ch#child{pid = undefined} | Chs];
-del_child(Name, [Ch|Chs]) ->
- [Ch|del_child(Name, Chs)];
-del_child(_, []) ->
- [].
-
-%% Chs = [S4, S3, Ch, S1, S0]
-%% Ret: {[S4, S3, Ch], [S1, S0]}
-split_child(Name, Chs) ->
- split_child(Name, Chs, []).
-
-split_child(Name, [Ch|Chs], After) when Ch#child.name =:= Name ->
- {lists:reverse([Ch#child{pid = undefined} | After]), Chs};
-split_child(Pid, [Ch|Chs], After) when Ch#child.pid =:= Pid ->
- {lists:reverse([Ch#child{pid = undefined} | After]), Chs};
-split_child(Name, [Ch|Chs], After) ->
- split_child(Name, Chs, [Ch | After]);
-split_child(_, [], After) ->
- {lists:reverse(After), []}.
-
-get_child(Name, State) ->
- get_child(Name, State, false).
-get_child(Pid, State, AllowPid) when AllowPid, is_pid(Pid) ->
- get_dynamic_child(Pid, State);
-get_child(Name, State, _) ->
- lists:keysearch(Name, #child.name, State#state.children).
-
-get_dynamic_child(Pid, #state{children=[Child], dynamics=Dynamics}) ->
- DynamicsDb = dynamics_db(Child#child.restart_type, Dynamics),
- case is_dynamic_pid(Pid, DynamicsDb) of
- true ->
- {value, Child#child{pid=Pid}};
- false ->
- RPid = restarting(Pid),
- case is_dynamic_pid(RPid, DynamicsDb) of
- true ->
- {value, Child#child{pid=RPid}};
- false ->
- case erlang:is_process_alive(Pid) of
- true -> false;
- false -> {value, Child}
- end
- end
- end.
-
-is_dynamic_pid(Pid, Dynamics) ->
- case ?SETS:is_set(Dynamics) of
- true ->
- ?SETS:is_element(Pid, Dynamics);
- false ->
- ?DICT:is_key(Pid, Dynamics)
- end.
-
-replace_child(Child, State) ->
- Chs = do_replace_child(Child, State#state.children),
- State#state{children = Chs}.
-
-do_replace_child(Child, [Ch|Chs]) when Ch#child.name =:= Child#child.name ->
- [Child | Chs];
-do_replace_child(Child, [Ch|Chs]) ->
- [Ch|do_replace_child(Child, Chs)].
-
-remove_child(Child, State) ->
- Chs = lists:keydelete(Child#child.name, #child.name, State#state.children),
- State#state{children = Chs}.
-
-%%-----------------------------------------------------------------
-%% Func: init_state/4
-%% Args: SupName = {local, atom()} | {global, atom()} | self
-%% Type = {Strategy, MaxIntensity, Period}
-%% Strategy = one_for_one | one_for_all | simple_one_for_one |
-%% rest_for_one
-%% MaxIntensity = integer() >= 0
-%% Period = integer() > 0
-%% Mod :== atom()
-%% Args :== term()
-%% Purpose: Check that Type is of correct type (!)
-%% Returns: {ok, state()} | Error
-%%-----------------------------------------------------------------
-init_state(SupName, Type, Mod, Args) ->
- case catch init_state1(SupName, Type, Mod, Args) of
- {ok, State} ->
- {ok, State};
- Error ->
- Error
- end.
-
-init_state1(SupName, {Strategy, MaxIntensity, Period}, Mod, Args) ->
- validStrategy(Strategy),
- validIntensity(MaxIntensity),
- validPeriod(Period),
- {ok, #state{name = supname(SupName,Mod),
- strategy = Strategy,
- intensity = MaxIntensity,
- period = Period,
- module = Mod,
- args = Args}};
-init_state1(_SupName, Type, _, _) ->
- {invalid_type, Type}.
-
-validStrategy(simple_one_for_one) -> true;
-validStrategy(one_for_one) -> true;
-validStrategy(one_for_all) -> true;
-validStrategy(rest_for_one) -> true;
-validStrategy(What) -> throw({invalid_strategy, What}).
-
-validIntensity(Max) when is_integer(Max),
- Max >= 0 -> true;
-validIntensity(What) -> throw({invalid_intensity, What}).
-
-validPeriod(Period) when is_integer(Period),
- Period > 0 -> true;
-validPeriod(What) -> throw({invalid_period, What}).
-
-supname(self, Mod) -> {self(), Mod};
-supname(N, _) -> N.
-
-%%% ------------------------------------------------------
-%%% Check that the children start specification is valid.
-%%% Shall be a six (6) tuple
-%%% {Name, Func, RestartType, Shutdown, ChildType, Modules}
-%%% where Name is an atom
-%%% Func is {Mod, Fun, Args} == {atom(), atom(), list()}
-%%% RestartType is permanent | temporary | transient |
-%%% intrinsic | {permanent, Delay} |
-%%% {transient, Delay} | {intrinsic, Delay}
-%% where Delay >= 0
-%%% Shutdown = integer() > 0 | infinity | brutal_kill
-%%% ChildType = supervisor | worker
-%%% Modules = [atom()] | dynamic
-%%% Returns: {ok, [child_rec()]} | Error
-%%% ------------------------------------------------------
-
-check_startspec(Children) -> check_startspec(Children, []).
-
-check_startspec([ChildSpec|T], Res) ->
- case check_childspec(ChildSpec) of
- {ok, Child} ->
- case lists:keymember(Child#child.name, #child.name, Res) of
- true -> {duplicate_child_name, Child#child.name};
- false -> check_startspec(T, [Child | Res])
- end;
- Error -> Error
- end;
-check_startspec([], Res) ->
- {ok, lists:reverse(Res)}.
-
-check_childspec({Name, Func, RestartType, Shutdown, ChildType, Mods}) ->
- catch check_childspec(Name, Func, RestartType, Shutdown, ChildType, Mods);
-check_childspec(X) -> {invalid_child_spec, X}.
-
-check_childspec(Name, Func, RestartType, Shutdown, ChildType, Mods) ->
- validName(Name),
- validFunc(Func),
- validRestartType(RestartType),
- validChildType(ChildType),
- validShutdown(Shutdown, ChildType),
- validMods(Mods),
- {ok, #child{name = Name, mfargs = Func, restart_type = RestartType,
- shutdown = Shutdown, child_type = ChildType, modules = Mods}}.
-
-validChildType(supervisor) -> true;
-validChildType(worker) -> true;
-validChildType(What) -> throw({invalid_child_type, What}).
-
-validName(_Name) -> true.
-
-validFunc({M, F, A}) when is_atom(M),
- is_atom(F),
- is_list(A) -> true;
-validFunc(Func) -> throw({invalid_mfa, Func}).
-
-validRestartType(permanent) -> true;
-validRestartType(temporary) -> true;
-validRestartType(transient) -> true;
-validRestartType(intrinsic) -> true;
-validRestartType({permanent, Delay}) -> validDelay(Delay);
-validRestartType({intrinsic, Delay}) -> validDelay(Delay);
-validRestartType({transient, Delay}) -> validDelay(Delay);
-validRestartType(RestartType) -> throw({invalid_restart_type,
- RestartType}).
-
-validDelay(Delay) when is_number(Delay),
- Delay >= 0 -> true;
-validDelay(What) -> throw({invalid_delay, What}).
-
-validShutdown(Shutdown, _)
- when is_integer(Shutdown), Shutdown > 0 -> true;
-validShutdown(infinity, _) -> true;
-validShutdown(brutal_kill, _) -> true;
-validShutdown(Shutdown, _) -> throw({invalid_shutdown, Shutdown}).
-
-validMods(dynamic) -> true;
-validMods(Mods) when is_list(Mods) ->
- lists:foreach(fun(Mod) ->
- if
- is_atom(Mod) -> ok;
- true -> throw({invalid_module, Mod})
- end
- end,
- Mods);
-validMods(Mods) -> throw({invalid_modules, Mods}).
-
-%%% ------------------------------------------------------
-%%% Add a new restart and calculate if the max restart
-%%% intensity has been reached (in that case the supervisor
-%%% shall terminate).
-%%% All restarts accured inside the period amount of seconds
-%%% are kept in the #state.restarts list.
-%%% Returns: {ok, State'} | {terminate, State'}
-%%% ------------------------------------------------------
-
-add_restart(State) ->
- I = State#state.intensity,
- P = State#state.period,
- R = State#state.restarts,
- Now = time_compat:monotonic_time(),
- R1 = add_restart([Now|R], Now, P),
- State1 = State#state{restarts = R1},
- case length(R1) of
- CurI when CurI =< I ->
- {ok, State1};
- _ ->
- {terminate, State1}
- end.
-
-add_restart([R|Restarts], Now, Period) ->
- case inPeriod(R, Now, Period) of
- true ->
- [R|add_restart(Restarts, Now, Period)];
- _ ->
- []
- end;
-add_restart([], _, _) ->
- [].
-
-inPeriod(Time, Now, Period) ->
- case time_compat:convert_time_unit(Now - Time, native, seconds) of
- T when T > Period ->
- false;
- _ ->
- true
- end.
-
-%%% ------------------------------------------------------
-%%% Error and progress reporting.
-%%% ------------------------------------------------------
-
-report_error(Error, Reason, Child, SupName) ->
- ErrorMsg = [{supervisor, SupName},
- {errorContext, Error},
- {reason, Reason},
- {offender, extract_child(Child)}],
- error_logger:error_report(supervisor_report, ErrorMsg).
-
-
-extract_child(Child) when is_list(Child#child.pid) ->
- [{nb_children, length(Child#child.pid)},
- {name, Child#child.name},
- {mfargs, Child#child.mfargs},
- {restart_type, Child#child.restart_type},
- {shutdown, Child#child.shutdown},
- {child_type, Child#child.child_type}];
-extract_child(Child) ->
- [{pid, Child#child.pid},
- {name, Child#child.name},
- {mfargs, Child#child.mfargs},
- {restart_type, Child#child.restart_type},
- {shutdown, Child#child.shutdown},
- {child_type, Child#child.child_type}].
-
-report_progress(Child, SupName) ->
- Progress = [{supervisor, SupName},
- {started, extract_child(Child)}],
- error_logger:info_report(progress, Progress).
diff --git a/src/time_compat.erl b/src/time_compat.erl
deleted file mode 100644
index b87c6cc550..0000000000
--- a/src/time_compat.erl
+++ /dev/null
@@ -1,305 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2014-2015. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
-
-%%
-%% If your code need to be able to execute on ERTS versions both
-%% earlier and later than 7.0, the best approach is to use the new
-%% time API introduced in ERTS 7.0 and implement a fallback
-%% solution using the old primitives to be used on old ERTS
-%% versions. This way your code can automatically take advantage
-%% of the improvements in the API when available. This is an
-%% example of how to implement such an API, but it can be used
-%% as is if you want to. Just add (a preferrably renamed version of)
-%% this module to your project, and call the API via this module
-%% instead of calling the BIFs directly.
-%%
-
--module(time_compat).
-
-%% We don't want warnings about the use of erlang:now/0 in
-%% this module.
--compile(nowarn_deprecated_function).
-%%
-%% We don't use
-%% -compile({nowarn_deprecated_function, [{erlang, now, 0}]}).
-%% since this will produce warnings when compiled on systems
-%% where it has not yet been deprecated.
-%%
-
--export([monotonic_time/0,
- monotonic_time/1,
- erlang_system_time/0,
- erlang_system_time/1,
- os_system_time/0,
- os_system_time/1,
- time_offset/0,
- time_offset/1,
- convert_time_unit/3,
- timestamp/0,
- unique_integer/0,
- unique_integer/1,
- monitor/2,
- system_info/1,
- system_flag/2]).
-
-monotonic_time() ->
- try
- erlang:monotonic_time()
- catch
- error:undef ->
- %% Use Erlang system time as monotonic time
- erlang_system_time_fallback()
- end.
-
-monotonic_time(Unit) ->
- try
- erlang:monotonic_time(Unit)
- catch
- error:badarg ->
- erlang:error(badarg, [Unit]);
- error:undef ->
- %% Use Erlang system time as monotonic time
- STime = erlang_system_time_fallback(),
- try
- convert_time_unit_fallback(STime, native, Unit)
- catch
- error:bad_time_unit -> erlang:error(badarg, [Unit])
- end
- end.
-
-erlang_system_time() ->
- try
- erlang:system_time()
- catch
- error:undef ->
- erlang_system_time_fallback()
- end.
-
-erlang_system_time(Unit) ->
- try
- erlang:system_time(Unit)
- catch
- error:badarg ->
- erlang:error(badarg, [Unit]);
- error:undef ->
- STime = erlang_system_time_fallback(),
- try
- convert_time_unit_fallback(STime, native, Unit)
- catch
- error:bad_time_unit -> erlang:error(badarg, [Unit])
- end
- end.
-
-os_system_time() ->
- try
- os:system_time()
- catch
- error:undef ->
- os_system_time_fallback()
- end.
-
-os_system_time(Unit) ->
- try
- os:system_time(Unit)
- catch
- error:badarg ->
- erlang:error(badarg, [Unit]);
- error:undef ->
- STime = os_system_time_fallback(),
- try
- convert_time_unit_fallback(STime, native, Unit)
- catch
- error:bad_time_unit -> erlang:error(badarg, [Unit])
- end
- end.
-
-time_offset() ->
- try
- erlang:time_offset()
- catch
- error:undef ->
- %% Erlang system time and Erlang monotonic
- %% time are always aligned
- 0
- end.
-
-time_offset(Unit) ->
- try
- erlang:time_offset(Unit)
- catch
- error:badarg ->
- erlang:error(badarg, [Unit]);
- error:undef ->
- try
- _ = integer_time_unit(Unit)
- catch
- error:bad_time_unit -> erlang:error(badarg, [Unit])
- end,
- %% Erlang system time and Erlang monotonic
- %% time are always aligned
- 0
- end.
-
-convert_time_unit(Time, FromUnit, ToUnit) ->
- try
- erlang:convert_time_unit(Time, FromUnit, ToUnit)
- catch
- error:undef ->
- try
- convert_time_unit_fallback(Time, FromUnit, ToUnit)
- catch
- _:_ ->
- erlang:error(badarg, [Time, FromUnit, ToUnit])
- end;
- error:Error ->
- erlang:error(Error, [Time, FromUnit, ToUnit])
- end.
-
-timestamp() ->
- try
- erlang:timestamp()
- catch
- error:undef ->
- erlang:now()
- end.
-
-unique_integer() ->
- try
- erlang:unique_integer()
- catch
- error:undef ->
- {MS, S, US} = erlang:now(),
- (MS*1000000+S)*1000000+US
- end.
-
-unique_integer(Modifiers) ->
- try
- erlang:unique_integer(Modifiers)
- catch
- error:badarg ->
- erlang:error(badarg, [Modifiers]);
- error:undef ->
- case is_valid_modifier_list(Modifiers) of
- true ->
- %% now() converted to an integer
- %% fullfill the requirements of
- %% all modifiers: unique, positive,
- %% and monotonic...
- {MS, S, US} = erlang:now(),
- (MS*1000000+S)*1000000+US;
- false ->
- erlang:error(badarg, [Modifiers])
- end
- end.
-
-monitor(Type, Item) ->
- try
- erlang:monitor(Type, Item)
- catch
- error:Error ->
- case {Error, Type, Item} of
- {badarg, time_offset, clock_service} ->
- %% Time offset is final and will never change.
- %% Return a dummy reference, there will never
- %% be any need for 'CHANGE' messages...
- make_ref();
- _ ->
- erlang:error(Error, [Type, Item])
- end
- end.
-
-system_info(Item) ->
- try
- erlang:system_info(Item)
- catch
- error:badarg ->
- case Item of
- time_correction ->
- case erlang:system_info(tolerant_timeofday) of
- enabled -> true;
- disabled -> false
- end;
- time_warp_mode ->
- no_time_warp;
- time_offset ->
- final;
- NotSupArg when NotSupArg == os_monotonic_time_source;
- NotSupArg == os_system_time_source;
- NotSupArg == start_time;
- NotSupArg == end_time ->
- %% Cannot emulate this...
- erlang:error(notsup, [NotSupArg]);
- _ ->
- erlang:error(badarg, [Item])
- end;
- error:Error ->
- erlang:error(Error, [Item])
- end.
-
-system_flag(Flag, Value) ->
- try
- erlang:system_flag(Flag, Value)
- catch
- error:Error ->
- case {Error, Flag, Value} of
- {badarg, time_offset, finalize} ->
- %% Time offset is final
- final;
- _ ->
- erlang:error(Error, [Flag, Value])
- end
- end.
-
-%%
-%% Internal functions
-%%
-
-integer_time_unit(native) -> 1000*1000;
-integer_time_unit(nano_seconds) -> 1000*1000*1000;
-integer_time_unit(micro_seconds) -> 1000*1000;
-integer_time_unit(milli_seconds) -> 1000;
-integer_time_unit(seconds) -> 1;
-integer_time_unit(I) when is_integer(I), I > 0 -> I;
-integer_time_unit(BadRes) -> erlang:error(bad_time_unit, [BadRes]).
-
-erlang_system_time_fallback() ->
- {MS, S, US} = erlang:now(),
- (MS*1000000+S)*1000000+US.
-
-os_system_time_fallback() ->
- {MS, S, US} = os:timestamp(),
- (MS*1000000+S)*1000000+US.
-
-convert_time_unit_fallback(Time, FromUnit, ToUnit) ->
- FU = integer_time_unit(FromUnit),
- TU = integer_time_unit(ToUnit),
- case Time < 0 of
- true -> TU*Time - (FU - 1);
- false -> TU*Time
- end div FU.
-
-is_valid_modifier_list([positive|Ms]) ->
- is_valid_modifier_list(Ms);
-is_valid_modifier_list([monotonic|Ms]) ->
- is_valid_modifier_list(Ms);
-is_valid_modifier_list([]) ->
- true;
-is_valid_modifier_list(_) ->
- false.