Packages

amqp_client

3.9.21
4.2.1 4.2.0 retired 4.1.6 4.1.5 retired 4.1.5-rc.2 retired 4.1.5-rc.1 retired 4.1.5-1 4.0.3 4.0.3-rc.1 3.13.0-rc.2 3.13.0-rc.1 3.12.14 3.12.13 3.12.12 3.12.11 3.12.10 3.12.9 3.12.8 3.12.7 3.12.6 3.12.5 3.12.4 3.12.3 3.12.2 3.12.1 3.12.0 3.12.0-rc.4 3.11.28 3.11.27 3.11.26 3.11.25 3.11.24 3.11.23 3.11.22 3.11.21 3.11.20 3.11.19 3.11.18 3.11.17 3.11.13 3.11.12 3.11.11 3.11.10 3.11.9 3.11.8 3.11.7 3.11.6 3.11.5 3.11.4 3.11.3 3.11.2 3.11.1 3.11.0 3.11.0-rc.2 3.11.0-rc.1 3.11.0-1 3.10.20 3.10.19 3.10.18 3.10.17 3.10.16 3.10.15 3.10.14 3.10.13 3.10.12 3.10.11 3.10.10 3.10.9 3.10.8 3.10.7 3.10.6 3.10.5 3.10.4 3.10.3 3.10.2 3.10.1 3.10.0 3.10.0-rc.6 3.10.0-rc.5 3.9.29 3.9.28 3.9.27 3.9.26 3.9.25 3.9.24 3.9.23 3.9.22 3.9.21 3.9.20 3.9.19 3.9.18 3.9.17 3.9.16 3.9.15 3.9.11 3.9.8 3.9.5 3.9.4 3.9.1 3.8.35 3.8.34 3.8.33 3.8.32 3.8.31 3.8.30 3.8.25 3.8.21 3.8.19 3.8.14 3.8.11 3.8.10 3.8.10-rc.1 3.8.9 3.8.7 3.8.6 3.8.6-rc.2 3.8.6-rc.1 3.8.5 3.8.5-rc.2 3.8.5-rc.1 3.8.4 3.8.4-rc.3 3.8.4-rc.1 3.8.3 3.8.3-rc.2 3.8.3-rc.1 3.8.2 3.8.2-rc.1 3.8.1 3.8.1-rc.1 3.8.0 3.8.0-rc.3 3.8.0-rc.2 3.8.0-rc.1 3.7.28 3.7.27 3.7.27-rc.2 3.7.27-rc.1 3.7.26 3.7.25 3.7.25-rc.1 3.7.24 3.7.24-rc.2 3.7.24-rc.1 3.7.23 3.7.23-rc.1 3.7.22 3.7.22-rc.2 3.7.22-rc.1 3.7.21 3.7.20 3.7.20-rc.2 3.7.20-rc.1 3.7.19 3.7.18 3.7.18-rc.1 3.7.17 3.7.17-rc.3 3.7.17-rc.2 3.7.15 3.7.14 3.7.14-rc.2 3.7.14-rc.1 3.7.13 3.7.13-rc.2 3.7.13-rc.1 3.7.12 3.7.12-rc.2 3.7.12-rc.1 3.7.11 3.7.11-rc.2 3.7.11-rc.1 3.7.10-rc.4 3.7.10-rc.3 3.7.10-rc.2 3.7.10-rc.1 3.7.9 3.7.9-rc.3 3.7.9-rc.2 3.7.8 3.7.8-rc.4 3.7.8-rc.3 3.7.8-rc.2 3.7.8-rc.1 3.7.7 3.7.7-rc.2 3.7.7-rc.1 3.7.6 3.7.6-rc.2 3.7.6-rc.1 3.7.5 3.7.5-rc.1 3.7.4 3.7.4-rc.4 3.7.4-rc.3 3.7.4-rc.2 3.7.4-rc.1 3.7.3 3.7.3-rc.2 3.7.3-rc.1 3.7.2 3.7.1 3.7.0-rc.2 3.6.16 3.6.16-rc.1 3.6.15 3.6.15-rc.1 3.6.14 3.6.13 3.6.12 3.6.11 3.6.10 3.6.9 3.6.8 3.6.7 3.6.7-pre.1 3.5.6 3.5.0 3.4.0 3.3.5 3.0.2 0.0.0-rc.1

RabbitMQ AMQP Client

Current section

Files

Jump to
amqp_client src amqp_connection.erl
Raw

src/amqp_connection.erl

%% This Source Code Form is subject to the terms of the Mozilla Public
%% License, v. 2.0. If a copy of the MPL was not distributed with this
%% file, You can obtain one at https://mozilla.org/MPL/2.0/.
%%
%% Copyright (c) 2007-2021 VMware, Inc. or its affiliates. All rights reserved.
%%
%% @type close_reason(Type) = {shutdown, amqp_reason(Type)}.
%% @type amqp_reason(Type) = {Type, Code, Text}
%% Code = non_neg_integer()
%% Text = binary().
%% @doc This module is responsible for maintaining a connection to an AMQP
%% broker and manages channels within the connection. This module is used to
%% open and close connections to the broker as well as creating new channels
%% within a connection.<br/>
%% The connections and channels created by this module are supervised under
%% amqp_client's supervision tree. Please note that connections and channels
%% do not get restarted automatically by the supervision tree in the case of a
%% failure. If you need robust connections and channels, we recommend you use
%% Erlang monitors on the returned connection and channel PIDs.<br/>
%% <br/>
%% In case of a failure or an AMQP error, the connection process exits with a
%% meaningful exit reason:<br/>
%% <br/>
%% <table>
%% <tr>
%% <td><strong>Cause</strong></td>
%% <td><strong>Exit reason</strong></td>
%% </tr>
%% <tr>
%% <td>Any reason, where Code would have been 200 otherwise</td>
%% <td>```normal'''</td>
%% </tr>
%% <tr>
%% <td>User application calls amqp_connection:close/3</td>
%% <td>```close_reason(app_initiated_close)'''</td>
%% </tr>
%% <tr>
%% <td>Server closes connection (hard error)</td>
%% <td>```close_reason(server_initiated_close)'''</td>
%% </tr>
%% <tr>
%% <td>Server misbehaved (did not follow protocol)</td>
%% <td>```close_reason(server_misbehaved)'''</td>
%% </tr>
%% <tr>
%% <td>AMQP client internal error - usually caused by a channel exiting
%% with an unusual reason. This is usually accompanied by a more
%% detailed error log from the channel</td>
%% <td>```close_reason(internal_error)'''</td>
%% </tr>
%% <tr>
%% <td>Other error</td>
%% <td>(various error reasons, causing more detailed logging)</td>
%% </tr>
%% </table>
%% <br/>
%% See type definitions below.
-module(amqp_connection).
-include("amqp_client_internal.hrl").
-export([open_channel/1, open_channel/2, open_channel/3, register_blocked_handler/2]).
-export([start/1, start/2, close/1, close/2, close/3, close/4]).
-export([error_atom/1]).
-export([info/2, info_keys/1, info_keys/0]).
-export([connection_name/1, update_secret/3]).
-export([socket_adapter_info/2]).
-define(DEFAULT_CONSUMER, {amqp_selective_consumer, []}).
-define(PROTOCOL_SSL_PORT, (?PROTOCOL_PORT - 1)).
%%---------------------------------------------------------------------------
%% Type Definitions
%%---------------------------------------------------------------------------
%% @type amqp_adapter_info() = #amqp_adapter_info{}.
%% @type amqp_params_direct() = #amqp_params_direct{}.
%% As defined in amqp_client.hrl. It contains the following fields:
%% <ul>
%% <li>username :: binary() - The name of a user registered with the broker,
%% defaults to &lt;&lt;guest"&gt;&gt;</li>
%% <li>password :: binary() - The password of user, defaults to 'none'</li>
%% <li>virtual_host :: binary() - The name of a virtual host in the broker,
%% defaults to &lt;&lt;"/"&gt;&gt;</li>
%% <li>node :: atom() - The node the broker runs on (direct only)</li>
%% <li>adapter_info :: amqp_adapter_info() - Extra management information for if
%% this connection represents a non-AMQP network connection.</li>
%% <li>client_properties :: [{binary(), atom(), binary()}] - A list of extra
%% client properties to be sent to the server, defaults to []</li>
%% </ul>
%%
%% @type amqp_params_network() = #amqp_params_network{}.
%% As defined in amqp_client.hrl. It contains the following fields:
%% <ul>
%% <li>username :: binary() - The name of a user registered with the broker,
%% defaults to &lt;&lt;guest"&gt;&gt;</li>
%% <li>password :: binary() - The user's password, defaults to
%% &lt;&lt;"guest"&gt;&gt;</li>
%% <li>virtual_host :: binary() - The name of a virtual host in the broker,
%% defaults to &lt;&lt;"/"&gt;&gt;</li>
%% <li>host :: string() - The hostname of the broker,
%% defaults to "localhost" (network only)</li>
%% <li>port :: integer() - The port the broker is listening on,
%% defaults to 5672 (network only)</li>
%% <li>channel_max :: non_neg_integer() - The channel_max handshake parameter,
%% defaults to 0</li>
%% <li>frame_max :: non_neg_integer() - The frame_max handshake parameter,
%% defaults to 0 (network only)</li>
%% <li>heartbeat :: non_neg_integer() - The heartbeat interval in seconds,
%% defaults to 0 (turned off) (network only)</li>
%% <li>connection_timeout :: non_neg_integer() | 'infinity'
%% - The connection timeout in milliseconds,
%% defaults to 30000 (network only)</li>
%% <li>ssl_options :: term() - The second parameter to be used with the
%% ssl:connect/2 function, defaults to 'none' (network only)</li>
%% <li>client_properties :: [{binary(), atom(), binary()}] - A list of extra
%% client properties to be sent to the server, defaults to []</li>
%% <li>socket_options :: [any()] - Extra socket options. These are
%% appended to the default options. See
%% <a href="https://www.erlang.org/doc/man/inet.html#setopts-2">inet:setopts/2</a>
%% and <a href="https://www.erlang.org/doc/man/gen_tcp.html#connect-4">
%% gen_tcp:connect/4</a> for descriptions of the available options.</li>
%% </ul>
%%---------------------------------------------------------------------------
%% Starting a connection
%%---------------------------------------------------------------------------
%% @spec (Params) -> {ok, Connection} | {error, Error}
%% where
%% Params = amqp_params_network() | amqp_params_direct()
%% Connection = pid()
%% @doc same as {@link amqp_connection:start/2. start(Params, undefined)}
start(AmqpParams) ->
start(AmqpParams, undefined).
%% @spec (Params, ConnectionName) -> {ok, Connection} | {error, Error}
%% where
%% Params = amqp_params_network() | amqp_params_direct()
%% ConnectionName = undefined | binary()
%% Connection = pid()
%% @doc Starts a connection to an AMQP server. Use network params to
%% connect to a remote AMQP server or direct params for a direct
%% connection to a RabbitMQ server, assuming that the server is
%% running in the same process space. If the port is set to 'undefined',
%% the default ports will be selected depending on whether this is a
%% normal or an SSL connection.
%% If ConnectionName is binary - it will be added to client_properties as
%% user specified connection name.
start(AmqpParams, ConnName) when ConnName == undefined; is_binary(ConnName) ->
ensure_started(),
AmqpParams0 =
case AmqpParams of
#amqp_params_direct{password = Password} ->
AmqpParams#amqp_params_direct{password = credentials_obfuscation:encrypt(Password)};
#amqp_params_network{password = Password} ->
AmqpParams#amqp_params_network{password = credentials_obfuscation:encrypt(Password)}
end,
AmqpParams1 =
case AmqpParams0 of
#amqp_params_network{port = undefined, ssl_options = none} ->
AmqpParams0#amqp_params_network{port = ?PROTOCOL_PORT};
#amqp_params_network{port = undefined, ssl_options = _} ->
AmqpParams0#amqp_params_network{port = ?PROTOCOL_SSL_PORT};
_ ->
AmqpParams0
end,
AmqpParams2 = set_connection_name(ConnName, AmqpParams1),
AmqpParams3 = amqp_ssl:maybe_enhance_ssl_options(AmqpParams2),
ok = ensure_safe_call_timeout(AmqpParams3, amqp_util:call_timeout()),
{ok, _Sup, Connection} = amqp_sup:start_connection_sup(AmqpParams3),
amqp_gen_connection:connect(Connection).
set_connection_name(undefined, Params) -> Params;
set_connection_name(ConnName,
#amqp_params_network{client_properties = Props} = Params) ->
Params#amqp_params_network{
client_properties = [
{<<"connection_name">>, longstr, ConnName} | Props
]};
set_connection_name(ConnName,
#amqp_params_direct{client_properties = Props} = Params) ->
Params#amqp_params_direct{
client_properties = [
{<<"connection_name">>, longstr, ConnName} | Props
]}.
%% Usually the amqp_client application will already be running. We
%% check whether that is the case by invoking an undocumented function
%% which does not require a synchronous call to the application
%% controller. That way we don't risk a dead-lock if, say, the
%% application controller is in the process of shutting down the very
%% application which is making this call.
ensure_started() ->
[ensure_started(App) || App <- [syntax_tools, compiler, xmerl,
rabbit_common, amqp_client, credentials_obfuscation]].
ensure_started(App) ->
case is_pid(application_controller:get_master(App)) andalso amqp_sup:is_ready() of
true -> ok;
false -> case application:ensure_all_started(App) of
{ok, _} -> ok;
{error, _} = E -> throw(E)
end
end.
%%---------------------------------------------------------------------------
%% Commands
%%---------------------------------------------------------------------------
%% @doc Invokes open_channel(ConnectionPid, none,
%% {amqp_selective_consumer, []}). Opens a channel without having to
%% specify a channel number. This uses the default consumer
%% implementation.
open_channel(ConnectionPid) ->
open_channel(ConnectionPid, none, ?DEFAULT_CONSUMER).
%% @doc Invokes open_channel(ConnectionPid, none, Consumer).
%% Opens a channel without having to specify a channel number.
open_channel(ConnectionPid, {_, _} = Consumer) ->
open_channel(ConnectionPid, none, Consumer);
%% @doc Invokes open_channel(ConnectionPid, ChannelNumber,
%% {amqp_selective_consumer, []}). Opens a channel, using the default
%% consumer implementation.
open_channel(ConnectionPid, ChannelNumber)
when is_number(ChannelNumber) orelse ChannelNumber =:= none ->
open_channel(ConnectionPid, ChannelNumber, ?DEFAULT_CONSUMER).
%% @spec (ConnectionPid, ChannelNumber, Consumer) -> Result
%% where
%% ConnectionPid = pid()
%% ChannelNumber = pos_integer() | 'none'
%% Consumer = {ConsumerModule, ConsumerArgs}
%% ConsumerModule = atom()
%% ConsumerArgs = [any()]
%% Result = {ok, ChannelPid} | {error, Error}
%% ChannelPid = pid()
%% @doc Opens an AMQP channel.<br/>
%% Opens a channel, using a proposed channel number and a specific consumer
%% implementation.<br/>
%% ConsumerModule must implement the amqp_gen_consumer behaviour. ConsumerArgs
%% is passed as parameter to ConsumerModule:init/1.<br/>
%% This function assumes that an AMQP connection (networked or direct)
%% has already been successfully established.<br/>
%% ChannelNumber must be less than or equal to the negotiated
%% max_channel value, or less than or equal to ?MAX_CHANNEL_NUMBER
%% (65535) if the negotiated max_channel value is 0.<br/>
%% In the direct connection, max_channel is always 0.
open_channel(ConnectionPid, ChannelNumber,
{_ConsumerModule, _ConsumerArgs} = Consumer) ->
amqp_gen_connection:open_channel(ConnectionPid, ChannelNumber, Consumer).
%% @spec (ConnectionPid) -> ok | Error
%% where
%% ConnectionPid = pid()
%% @doc Closes the channel, invokes
%% close(Channel, 200, &lt;&lt;"Goodbye"&gt;&gt;).
close(ConnectionPid) ->
close(ConnectionPid, 200, <<"Goodbye">>).
%% @spec (ConnectionPid, Timeout) -> ok | Error
%% where
%% ConnectionPid = pid()
%% Timeout = integer()
%% @doc Closes the channel, using the supplied Timeout value.
close(ConnectionPid, Timeout) ->
close(ConnectionPid, 200, <<"Goodbye">>, Timeout).
%% @spec (ConnectionPid, Code, Text) -> ok | closing
%% where
%% ConnectionPid = pid()
%% Code = integer()
%% Text = binary()
%% @doc Closes the AMQP connection, allowing the caller to set the reply
%% code and text.
close(ConnectionPid, Code, Text) ->
close(ConnectionPid, Code, Text, amqp_util:call_timeout()).
%% @spec (ConnectionPid, Code, Text, Timeout) -> ok | closing
%% where
%% ConnectionPid = pid()
%% Code = integer()
%% Text = binary()
%% Timeout = integer()
%% @doc Closes the AMQP connection, allowing the caller to set the reply
%% code and text, as well as a timeout for the operation, after which the
%% connection will be abruptly terminated.
close(ConnectionPid, Code, Text, Timeout) ->
Close = #'connection.close'{reply_text = Text,
reply_code = Code,
class_id = 0,
method_id = 0},
amqp_gen_connection:close(ConnectionPid, Close, Timeout).
register_blocked_handler(ConnectionPid, BlockHandler) ->
amqp_gen_connection:register_blocked_handler(ConnectionPid, BlockHandler).
-spec update_secret(pid(), term(), binary()) ->
{'ok', rabbit_types:auth_user()} |
{'refused', string(), [any()]} |
{'error', any()}.
update_secret(ConnectionPid, NewSecret, Reason) ->
Update = #'connection.update_secret'{new_secret = NewSecret,
reason = Reason},
amqp_gen_connection:update_secret(ConnectionPid, Update).
%%---------------------------------------------------------------------------
%% Other functions
%%---------------------------------------------------------------------------
%% @spec (Code) -> atom()
%% where
%% Code = integer()
%% @doc Returns a descriptive atom corresponding to the given AMQP
%% error code.
error_atom(Code) -> ?PROTOCOL:amqp_exception(Code).
%% @spec (ConnectionPid, Items) -> ResultList
%% where
%% ConnectionPid = pid()
%% Items = [Item]
%% ResultList = [{Item, Result}]
%% Item = atom()
%% Result = term()
%% @doc Returns information about the connection, as specified by the Items
%% list. Item may be any atom returned by info_keys/1:
%%<ul>
%%<li>type - returns the type of the connection (network or direct)</li>
%%<li>server_properties - returns the server_properties fields sent by the
%% server while establishing the connection</li>
%%<li>is_closing - returns true if the connection is in the process of closing
%% and false otherwise</li>
%%<li>amqp_params - returns the #amqp_params{} structure used to start the
%% connection</li>
%%<li>num_channels - returns the number of channels currently open under the
%% connection (excluding channel 0)</li>
%%<li>channel_max - returns the channel_max value negotiated with the
%% server</li>
%%<li>heartbeat - returns the heartbeat value negotiated with the server
%% (only for the network connection)</li>
%%<li>frame_max - returns the frame_max value negotiated with the
%% server (only for the network connection)</li>
%%<li>sock - returns the socket for the network connection (for use with
%% e.g. inet:sockname/1) (only for the network connection)</li>
%%<li>any other value - throws an exception</li>
%%</ul>
info(ConnectionPid, Items) ->
amqp_gen_connection:info(ConnectionPid, Items).
%% @spec (ConnectionPid) -> Items
%% where
%% ConnectionPid = pid()
%% Items = [Item]
%% Item = atom()
%% @doc Returns a list of atoms that can be used in conjunction with info/2.
%% Note that the list differs from a type of connection to another (network vs.
%% direct). Use info_keys/0 to get a list of info keys that can be used for
%% any connection.
info_keys(ConnectionPid) ->
amqp_gen_connection:info_keys(ConnectionPid).
%% @spec () -> Items
%% where
%% Items = [Item]
%% Item = atom()
%% @doc Returns a list of atoms that can be used in conjunction with info/2.
%% These are general info keys, which can be used in any type of connection.
%% Other info keys may exist for a specific type. To get the full list of
%% atoms that can be used for a certain connection, use info_keys/1.
info_keys() ->
amqp_gen_connection:info_keys().
%% @doc Takes a socket and a protocol, returns an #amqp_adapter_info{}
%% based on the socket for the protocol given.
socket_adapter_info(Sock, Protocol) ->
amqp_direct_connection:socket_adapter_info(Sock, Protocol).
%% @spec (ConnectionPid) -> ConnectionName
%% where
%% ConnectionPid = pid()
%% ConnectionName = binary()
%% @doc Returns user specified connection name from client properties
connection_name(ConnectionPid) ->
ClientProperties = case info(ConnectionPid, [amqp_params]) of
[{_, #amqp_params_network{client_properties = Props}}] -> Props;
[{_, #amqp_params_direct{client_properties = Props}}] -> Props
end,
case lists:keyfind(<<"connection_name">>, 1, ClientProperties) of
{<<"connection_name">>, _, ConnName} -> ConnName;
false -> undefined
end.
ensure_safe_call_timeout(#amqp_params_network{connection_timeout = ConnTimeout}, CallTimeout) ->
maybe_update_call_timeout(ConnTimeout, CallTimeout);
ensure_safe_call_timeout(#amqp_params_direct{}, CallTimeout) ->
case net_kernel:get_net_ticktime() of
NetTicktime when is_integer(NetTicktime) ->
maybe_update_call_timeout(tick_or_direct_timeout(NetTicktime * 1000),
CallTimeout);
{ongoing_change_to, NetTicktime} ->
maybe_update_call_timeout(tick_or_direct_timeout(NetTicktime * 1000),
CallTimeout);
ignored ->
maybe_update_call_timeout(?DIRECT_OPERATION_TIMEOUT, CallTimeout)
end.
maybe_update_call_timeout(BaseTimeout, CallTimeout)
when is_integer(BaseTimeout), CallTimeout > BaseTimeout ->
ok;
maybe_update_call_timeout(BaseTimeout, CallTimeout) ->
EffectiveSafeCallTimeout = amqp_util:safe_call_timeout(BaseTimeout),
?LOG_WARN("AMQP 0-9-1 client call timeout was ~p ms, is updated to a safe effective "
"value of ~p ms", [CallTimeout, EffectiveSafeCallTimeout]),
amqp_util:update_call_timeout(EffectiveSafeCallTimeout),
ok.
tick_or_direct_timeout(Timeout) when Timeout >= ?DIRECT_OPERATION_TIMEOUT -> Timeout;
tick_or_direct_timeout(_Timeout) -> ?DIRECT_OPERATION_TIMEOUT.