├── .gitignore ├── .travis.yml ├── LICENSE ├── LICENSE-MPL-RabbitMQ ├── Makefile ├── README.md ├── erlang.mk ├── include ├── rabbit.hrl ├── rabbit_framing.hrl ├── rabbit_misc.hrl └── rabbit_msg_store.hrl ├── rebar ├── rebar.config └── src ├── app_utils.erl ├── code_version.erl ├── credit_flow.erl ├── gen_server2.erl ├── mirrored_supervisor.erl ├── mochijson2.erl ├── pmon.erl ├── priority_queue.erl ├── rabbit_amqqueue.erl ├── rabbit_auth_backend_dummy.erl ├── rabbit_auth_backend_internal.erl ├── rabbit_auth_mechanism.erl ├── rabbit_authn_backend.erl ├── rabbit_authz_backend.erl ├── rabbit_backing_queue.erl ├── rabbit_basic.erl ├── rabbit_binary_generator.erl ├── rabbit_binary_parser.erl ├── rabbit_channel.erl ├── rabbit_channel_interceptor.erl ├── rabbit_command_assembler.erl ├── rabbit_common.app.src ├── rabbit_control_misc.erl ├── rabbit_ct_broker_helpers.erl ├── rabbit_ct_helpers.erl ├── rabbit_data_coercion.erl ├── rabbit_error_logger_handler.erl ├── rabbit_event.erl ├── rabbit_exchange_decorator.erl ├── rabbit_exchange_type.erl ├── rabbit_framing_amqp_0_8.erl ├── rabbit_framing_amqp_0_9_1.erl ├── rabbit_health_check.erl ├── rabbit_heartbeat.erl ├── rabbit_misc.erl ├── rabbit_msg_store_index.erl ├── rabbit_net.erl ├── rabbit_networking.erl ├── rabbit_nodes.erl ├── rabbit_password_hashing.erl ├── rabbit_policy_validator.erl ├── rabbit_queue_collector.erl ├── rabbit_queue_decorator.erl ├── rabbit_queue_master_locator.erl ├── rabbit_reader.erl ├── rabbit_runtime_parameter.erl ├── rabbit_types.erl ├── rabbit_writer.erl ├── ssl_compat.erl ├── supervisor2.erl └── time_compat.erl /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | ebin/ 3 | deps/ 4 | .rebar/ 5 | *.txt 6 | .idea/ 7 | *.iml -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: erlang 2 | 3 | sudo: false 4 | 5 | otp_release: 6 | - R16B03 7 | - 17.5 8 | - 18.1 9 | 10 | script: "make" 11 | 12 | notifications: 13 | recipients: 14 | - jon@jbrisbin.com 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This package, the RabbitMQ server is licensed under the MPL. For the 2 | MPL, please see LICENSE-MPL-RabbitMQ. 3 | 4 | If you have any questions regarding licensing, please contact us at 5 | info@rabbitmq.com. 6 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PROJECT = rabbit_common 2 | 3 | ERLC_OPTS = +debug_info \ 4 | +compressed \ 5 | +report \ 6 | +warn_export_all \ 7 | +nowarn_export_vars \ 8 | +warn_shadow_vars \ 9 | +warn_unused_function \ 10 | +warn_deprecated_function \ 11 | +warn_obsolete_guard \ 12 | +warn_unused_import 13 | 14 | COMPILE_FIRST = gen_server2 \ 15 | supervisor2 \ 16 | mirrored_supervisor 17 | 18 | include erlang.mk 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rebar-friendly fork of Rabbit common 2 | 3 | This is a fork of the rabbit_common dependency, which is needed by the 4 | [official RabbitMQ/AMQP Erlang client](https://github.com/rabbitmq/rabbitmq-erlang-client). 5 | 6 | It's meant to be included in your rebar projects in your rebar.config file: 7 | 8 | {deps, [ 9 | {rabbit_common, ".*", {git, "git://github.com/jbrisbin/rabbit_common.git", "rabbitmq-3.5.6"}} 10 | ]}. 11 | 12 | The "master" branch of this port is a simple re-packaging of the rabbit_common AMQP client dependency. 13 | 14 | ### License 15 | 16 | This package, just like the the RabbitMQ server, is licensed under the MPL. For the MPL, please see LICENSE-MPL-RabbitMQ. 17 | -------------------------------------------------------------------------------- /include/rabbit.hrl: -------------------------------------------------------------------------------- 1 | %% The contents of this file are subject to the Mozilla Public License 2 | %% Version 1.1 (the "License"); you may not use this file except in 3 | %% compliance with the License. You may obtain a copy of the License 4 | %% at http://www.mozilla.org/MPL/ 5 | %% 6 | %% Software distributed under the License is distributed on an "AS IS" 7 | %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 8 | %% the License for the specific language governing rights and 9 | %% limitations under the License. 10 | %% 11 | %% The Original Code is RabbitMQ. 12 | %% 13 | %% The Initial Developer of the Original Code is Pivotal Software, Inc. 14 | %% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. 15 | %% 16 | 17 | %% Passed around most places 18 | -record(user, {username, 19 | tags, 20 | authz_backends}). %% List of {Module, AuthUserImpl} pairs 21 | 22 | %% Passed to auth backends 23 | -record(auth_user, {username, 24 | tags, 25 | impl}). 26 | %% Passed to authz backends. 27 | -record(authz_socket_info, {sockname, peername}). 28 | 29 | %% Implementation for the internal auth backend 30 | -record(internal_user, { 31 | username, 32 | password_hash, 33 | tags, 34 | %% password hashing implementation module, 35 | %% typically rabbit_password_hashing_* but can 36 | %% come from a plugin 37 | hashing_algorithm}). 38 | -record(permission, {configure, write, read}). 39 | -record(user_vhost, {username, virtual_host}). 40 | -record(user_permission, {user_vhost, permission}). 41 | 42 | -record(vhost, {virtual_host, dummy}). 43 | 44 | -record(content, 45 | {class_id, 46 | properties, %% either 'none', or a decoded record/tuple 47 | properties_bin, %% either 'none', or an encoded properties binary 48 | %% Note: at most one of properties and properties_bin can be 49 | %% 'none' at once. 50 | protocol, %% The protocol under which properties_bin was encoded 51 | payload_fragments_rev %% list of binaries, in reverse order (!) 52 | }). 53 | 54 | -record(resource, { 55 | virtual_host, 56 | %% exchange, queue, ... 57 | kind, 58 | %% name as a binary 59 | name 60 | }). 61 | 62 | %% fields described as 'transient' here are cleared when writing to 63 | %% rabbit_durable_ 64 | -record(exchange, { 65 | name, type, durable, auto_delete, internal, arguments, %% immutable 66 | scratches, %% durable, explicitly updated via update_scratch/3 67 | policy, %% durable, implicitly updated when policy changes 68 | decorators}). %% transient, recalculated in store/1 (i.e. recovery) 69 | 70 | -record(amqqueue, { 71 | name, durable, auto_delete, exclusive_owner = none, %% immutable 72 | arguments, %% immutable 73 | pid, %% durable (just so we know home node) 74 | slave_pids, sync_slave_pids, %% transient 75 | recoverable_slaves, %% durable 76 | policy, %% durable, implicit update as above 77 | gm_pids, %% transient 78 | decorators, %% transient, recalculated as above 79 | state}). %% durable (have we crashed?) 80 | 81 | -record(exchange_serial, {name, next}). 82 | 83 | %% mnesia doesn't like unary records, so we add a dummy 'value' field 84 | -record(route, {binding, value = const}). 85 | -record(reverse_route, {reverse_binding, value = const}). 86 | 87 | -record(binding, {source, key, destination, args = []}). 88 | -record(reverse_binding, {destination, key, source, args = []}). 89 | 90 | -record(topic_trie_node, {trie_node, edge_count, binding_count}). 91 | -record(topic_trie_edge, {trie_edge, node_id}). 92 | -record(topic_trie_binding, {trie_binding, value = const}). 93 | 94 | -record(trie_node, {exchange_name, node_id}). 95 | -record(trie_edge, {exchange_name, node_id, word}). 96 | -record(trie_binding, {exchange_name, node_id, destination, arguments}). 97 | 98 | -record(listener, {node, protocol, host, ip_address, port}). 99 | 100 | -record(runtime_parameters, {key, value}). 101 | 102 | -record(basic_message, 103 | {exchange_name, %% The exchange where the message was received 104 | routing_keys = [], %% Routing keys used during publish 105 | content, %% The message content 106 | id, %% A `rabbit_guid:gen()` generated id 107 | is_persistent}). %% Whether the message was published as persistent 108 | 109 | -record(delivery, 110 | {mandatory, %% Whether the message was published as mandatory 111 | confirm, %% Whether the message needs confirming 112 | sender, %% The pid of the process that created the delivery 113 | message, %% The #basic_message record 114 | msg_seq_no, %% Msg Sequence Number from the channel publish_seqno field 115 | flow}). %% Should flow control be used for this delivery 116 | 117 | -record(amqp_error, {name, explanation = "", method = none}). 118 | 119 | -record(event, {type, props, reference = undefined, timestamp}). 120 | 121 | -record(message_properties, {expiry, needs_confirming = false, size}). 122 | 123 | -record(plugin, {name, %% atom() 124 | version, %% string() 125 | description, %% string() 126 | type, %% 'ez' or 'dir' 127 | dependencies, %% [{atom(), string()}] 128 | location}). %% string() 129 | 130 | %%---------------------------------------------------------------------------- 131 | 132 | -define(COPYRIGHT_MESSAGE, "Copyright (C) 2007-2016 Pivotal Software, Inc."). 133 | -define(INFORMATION_MESSAGE, "Licensed under the MPL. See http://www.rabbitmq.com/"). 134 | -define(OTP_MINIMUM, "R16B03"). 135 | -define(ERTS_MINIMUM, "5.10.4"). 136 | 137 | %% EMPTY_FRAME_SIZE, 8 = 1 + 2 + 4 + 1 138 | %% - 1 byte of frame type 139 | %% - 2 bytes of channel number 140 | %% - 4 bytes of frame payload length 141 | %% - 1 byte of payload trailer FRAME_END byte 142 | %% See rabbit_binary_generator:check_empty_frame_size/0, an assertion 143 | %% called at startup. 144 | -define(EMPTY_FRAME_SIZE, 8). 145 | 146 | -define(MAX_WAIT, 16#ffffffff). 147 | 148 | -define(HIBERNATE_AFTER_MIN, 1000). 149 | -define(DESIRED_HIBERNATE, 10000). 150 | -define(CREDIT_DISC_BOUND, {2000, 500}). 151 | %% When we discover that we should write some indices to disk for some 152 | %% betas, the IO_BATCH_SIZE sets the number of betas that we must be 153 | %% due to write indices for before we do any work at all. 154 | -define(IO_BATCH_SIZE, 2048). %% next power-of-2 after ?CREDIT_DISC_BOUND 155 | 156 | -define(INVALID_HEADERS_KEY, <<"x-invalid-headers">>). 157 | -define(ROUTING_HEADERS, [<<"CC">>, <<"BCC">>]). 158 | -define(DELETED_HEADER, <<"BCC">>). 159 | 160 | -define(EXCHANGE_DELETE_IN_PROGRESS_COMPONENT, <<"exchange-delete-in-progress">>). 161 | 162 | -define(CHANNEL_OPERATION_TIMEOUT, rabbit_misc:get_channel_operation_timeout()). 163 | 164 | %% Trying to send a term across a cluster larger than 2^31 bytes will 165 | %% cause the VM to exit with "Absurdly large distribution output data 166 | %% buffer". So we limit the max message size to 2^31 - 10^6 bytes (1MB 167 | %% to allow plenty of leeway for the #basic_message{} and #content{} 168 | %% wrapping the message body). 169 | -define(MAX_MSG_SIZE, 2147383648). 170 | 171 | %% First number is maximum size in bytes before we start to 172 | %% truncate. The following 4-tuple is: 173 | %% 174 | %% 1) Maximum size of printable lists and binaries. 175 | %% 2) Maximum size of any structural term. 176 | %% 3) Amount to decrease 1) every time we descend while truncating. 177 | %% 4) Amount to decrease 2) every time we descend while truncating. 178 | %% 179 | %% Whole thing feeds into truncate:log_event/2. 180 | -define(LOG_TRUNC, {100000, {2000, 100, 50, 5}}). 181 | 182 | -define(store_proc_name(N), rabbit_misc:store_proc_name(?MODULE, N)). 183 | -------------------------------------------------------------------------------- /include/rabbit_framing.hrl: -------------------------------------------------------------------------------- 1 | %% Autogenerated code. Do not edit. 2 | %% 3 | %% The contents of this file are subject to the Mozilla Public License 4 | %% Version 1.1 (the "License"); you may not use this file except in 5 | %% compliance with the License. You may obtain a copy of the License 6 | %% at http://www.mozilla.org/MPL/ 7 | %% 8 | %% Software distributed under the License is distributed on an "AS IS" 9 | %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 10 | %% the License for the specific language governing rights and 11 | %% limitations under the License. 12 | %% 13 | %% The Original Code is RabbitMQ. 14 | %% 15 | %% The Initial Developer of the Original Code is Pivotal Software, Inc. 16 | %% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. 17 | %% 18 | -define(PROTOCOL_PORT, 5672). 19 | -define(FRAME_METHOD, 1). 20 | -define(FRAME_HEADER, 2). 21 | -define(FRAME_BODY, 3). 22 | -define(FRAME_HEARTBEAT, 8). 23 | -define(FRAME_MIN_SIZE, 4096). 24 | -define(FRAME_END, 206). 25 | -define(REPLY_SUCCESS, 200). 26 | -define(CONTENT_TOO_LARGE, 311). 27 | -define(NO_ROUTE, 312). 28 | -define(NO_CONSUMERS, 313). 29 | -define(ACCESS_REFUSED, 403). 30 | -define(NOT_FOUND, 404). 31 | -define(RESOURCE_LOCKED, 405). 32 | -define(PRECONDITION_FAILED, 406). 33 | -define(CONNECTION_FORCED, 320). 34 | -define(INVALID_PATH, 402). 35 | -define(FRAME_ERROR, 501). 36 | -define(SYNTAX_ERROR, 502). 37 | -define(COMMAND_INVALID, 503). 38 | -define(CHANNEL_ERROR, 504). 39 | -define(UNEXPECTED_FRAME, 505). 40 | -define(RESOURCE_ERROR, 506). 41 | -define(NOT_ALLOWED, 530). 42 | -define(NOT_IMPLEMENTED, 540). 43 | -define(INTERNAL_ERROR, 541). 44 | -define(FRAME_OOB_METHOD, 4). 45 | -define(FRAME_OOB_HEADER, 5). 46 | -define(FRAME_OOB_BODY, 6). 47 | -define(FRAME_TRACE, 7). 48 | -define(NOT_DELIVERED, 310). 49 | %% Method field records. 50 | -record('connection.start', {version_major = 0, version_minor = 9, server_properties, mechanisms = <<"PLAIN">>, locales = <<"en_US">>}). 51 | -record('connection.start_ok', {client_properties, mechanism = <<"PLAIN">>, response, locale = <<"en_US">>}). 52 | -record('connection.secure', {challenge}). 53 | -record('connection.secure_ok', {response}). 54 | -record('connection.tune', {channel_max = 0, frame_max = 0, heartbeat = 0}). 55 | -record('connection.tune_ok', {channel_max = 0, frame_max = 0, heartbeat = 0}). 56 | -record('connection.open', {virtual_host = <<"/">>, capabilities = <<"">>, insist = false}). 57 | -record('connection.open_ok', {known_hosts = <<"">>}). 58 | -record('connection.close', {reply_code, reply_text = <<"">>, class_id, method_id}). 59 | -record('connection.close_ok', {}). 60 | -record('connection.blocked', {reason = <<"">>}). 61 | -record('connection.unblocked', {}). 62 | -record('connection.redirect', {host, known_hosts = <<"">>}). 63 | -record('channel.open', {out_of_band = <<"">>}). 64 | -record('channel.open_ok', {channel_id = <<"">>}). 65 | -record('channel.flow', {active}). 66 | -record('channel.flow_ok', {active}). 67 | -record('channel.close', {reply_code, reply_text = <<"">>, class_id, method_id}). 68 | -record('channel.close_ok', {}). 69 | -record('channel.alert', {reply_code, reply_text = <<"">>, details = []}). 70 | -record('access.request', {realm = <<"/data">>, exclusive = false, passive = true, active = true, write = true, read = true}). 71 | -record('access.request_ok', {ticket = 1}). 72 | -record('exchange.declare', {ticket = 0, exchange, type = <<"direct">>, passive = false, durable = false, auto_delete = false, internal = false, nowait = false, arguments = []}). 73 | -record('exchange.declare_ok', {}). 74 | -record('exchange.delete', {ticket = 0, exchange, if_unused = false, nowait = false}). 75 | -record('exchange.delete_ok', {}). 76 | -record('exchange.bind', {ticket = 0, destination, source, routing_key = <<"">>, nowait = false, arguments = []}). 77 | -record('exchange.bind_ok', {}). 78 | -record('exchange.unbind', {ticket = 0, destination, source, routing_key = <<"">>, nowait = false, arguments = []}). 79 | -record('exchange.unbind_ok', {}). 80 | -record('queue.declare', {ticket = 0, queue = <<"">>, passive = false, durable = false, exclusive = false, auto_delete = false, nowait = false, arguments = []}). 81 | -record('queue.declare_ok', {queue, message_count, consumer_count}). 82 | -record('queue.bind', {ticket = 0, queue = <<"">>, exchange, routing_key = <<"">>, nowait = false, arguments = []}). 83 | -record('queue.bind_ok', {}). 84 | -record('queue.purge', {ticket = 0, queue = <<"">>, nowait = false}). 85 | -record('queue.purge_ok', {message_count}). 86 | -record('queue.delete', {ticket = 0, queue = <<"">>, if_unused = false, if_empty = false, nowait = false}). 87 | -record('queue.delete_ok', {message_count}). 88 | -record('queue.unbind', {ticket = 0, queue = <<"">>, exchange, routing_key = <<"">>, arguments = []}). 89 | -record('queue.unbind_ok', {}). 90 | -record('basic.qos', {prefetch_size = 0, prefetch_count = 0, global = false}). 91 | -record('basic.qos_ok', {}). 92 | -record('basic.consume', {ticket = 0, queue = <<"">>, consumer_tag = <<"">>, no_local = false, no_ack = false, exclusive = false, nowait = false, arguments = []}). 93 | -record('basic.consume_ok', {consumer_tag}). 94 | -record('basic.cancel', {consumer_tag, nowait = false}). 95 | -record('basic.cancel_ok', {consumer_tag}). 96 | -record('basic.publish', {ticket = 0, exchange = <<"">>, routing_key = <<"">>, mandatory = false, immediate = false}). 97 | -record('basic.return', {reply_code, reply_text = <<"">>, exchange, routing_key}). 98 | -record('basic.deliver', {consumer_tag, delivery_tag, redelivered = false, exchange, routing_key}). 99 | -record('basic.get', {ticket = 0, queue = <<"">>, no_ack = false}). 100 | -record('basic.get_ok', {delivery_tag, redelivered = false, exchange, routing_key, message_count}). 101 | -record('basic.get_empty', {cluster_id = <<"">>}). 102 | -record('basic.ack', {delivery_tag = 0, multiple = false}). 103 | -record('basic.reject', {delivery_tag, requeue = true}). 104 | -record('basic.recover_async', {requeue = false}). 105 | -record('basic.recover', {requeue = false}). 106 | -record('basic.recover_ok', {}). 107 | -record('basic.nack', {delivery_tag = 0, multiple = false, requeue = true}). 108 | -record('basic.credit', {consumer_tag = <<"">>, credit, drain}). 109 | -record('basic.credit_ok', {available}). 110 | -record('basic.credit_drained', {consumer_tag = <<"">>, credit_drained}). 111 | -record('tx.select', {}). 112 | -record('tx.select_ok', {}). 113 | -record('tx.commit', {}). 114 | -record('tx.commit_ok', {}). 115 | -record('tx.rollback', {}). 116 | -record('tx.rollback_ok', {}). 117 | -record('confirm.select', {nowait = false}). 118 | -record('confirm.select_ok', {}). 119 | -record('file.qos', {prefetch_size = 0, prefetch_count = 0, global = false}). 120 | -record('file.qos_ok', {}). 121 | -record('file.consume', {ticket = 1, queue = <<"">>, consumer_tag = <<"">>, no_local = false, no_ack = false, exclusive = false, nowait = false}). 122 | -record('file.consume_ok', {consumer_tag}). 123 | -record('file.cancel', {consumer_tag, nowait = false}). 124 | -record('file.cancel_ok', {consumer_tag}). 125 | -record('file.open', {identifier, content_size}). 126 | -record('file.open_ok', {staged_size}). 127 | -record('file.stage', {}). 128 | -record('file.publish', {ticket = 1, exchange = <<"">>, routing_key = <<"">>, mandatory = false, immediate = false, identifier}). 129 | -record('file.return', {reply_code = 200, reply_text = <<"">>, exchange, routing_key}). 130 | -record('file.deliver', {consumer_tag, delivery_tag, redelivered = false, exchange, routing_key, identifier}). 131 | -record('file.ack', {delivery_tag = 0, multiple = false}). 132 | -record('file.reject', {delivery_tag, requeue = true}). 133 | -record('stream.qos', {prefetch_size = 0, prefetch_count = 0, consume_rate = 0, global = false}). 134 | -record('stream.qos_ok', {}). 135 | -record('stream.consume', {ticket = 1, queue = <<"">>, consumer_tag = <<"">>, no_local = false, exclusive = false, nowait = false}). 136 | -record('stream.consume_ok', {consumer_tag}). 137 | -record('stream.cancel', {consumer_tag, nowait = false}). 138 | -record('stream.cancel_ok', {consumer_tag}). 139 | -record('stream.publish', {ticket = 1, exchange = <<"">>, routing_key = <<"">>, mandatory = false, immediate = false}). 140 | -record('stream.return', {reply_code = 200, reply_text = <<"">>, exchange, routing_key}). 141 | -record('stream.deliver', {consumer_tag, delivery_tag, exchange, queue}). 142 | -record('dtx.select', {}). 143 | -record('dtx.select_ok', {}). 144 | -record('dtx.start', {dtx_identifier}). 145 | -record('dtx.start_ok', {}). 146 | -record('tunnel.request', {meta_data}). 147 | -record('test.integer', {integer_1, integer_2, integer_3, integer_4, operation}). 148 | -record('test.integer_ok', {result}). 149 | -record('test.string', {string_1, string_2, operation}). 150 | -record('test.string_ok', {result}). 151 | -record('test.table', {table, integer_op, string_op}). 152 | -record('test.table_ok', {integer_result, string_result}). 153 | -record('test.content', {}). 154 | -record('test.content_ok', {content_checksum}). 155 | %% Class property records. 156 | -record('P_connection', {}). 157 | -record('P_channel', {}). 158 | -record('P_access', {}). 159 | -record('P_exchange', {}). 160 | -record('P_queue', {}). 161 | -record('P_basic', {content_type, content_encoding, headers, delivery_mode, priority, correlation_id, reply_to, expiration, message_id, timestamp, type, user_id, app_id, cluster_id}). 162 | -record('P_tx', {}). 163 | -record('P_confirm', {}). 164 | -record('P_file', {content_type, content_encoding, headers, priority, reply_to, message_id, filename, timestamp, cluster_id}). 165 | -record('P_stream', {content_type, content_encoding, headers, priority, timestamp}). 166 | -record('P_dtx', {}). 167 | -record('P_tunnel', {headers, proxy_name, data_name, durable, broadcast}). 168 | -record('P_test', {}). 169 | -------------------------------------------------------------------------------- /include/rabbit_misc.hrl: -------------------------------------------------------------------------------- 1 | %% The contents of this file are subject to the Mozilla Public License 2 | %% Version 1.1 (the "License"); you may not use this file except in 3 | %% compliance with the License. You may obtain a copy of the License 4 | %% at http://www.mozilla.org/MPL/ 5 | %% 6 | %% Software distributed under the License is distributed on an "AS IS" 7 | %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 8 | %% the License for the specific language governing rights and 9 | %% limitations under the License. 10 | %% 11 | %% The Original Code is RabbitMQ. 12 | %% 13 | %% The Initial Developer of the Original Code is GoPivotal, Inc. 14 | %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved. 15 | %% 16 | 17 | -define(RPC_TIMEOUT, infinity). 18 | -------------------------------------------------------------------------------- /include/rabbit_msg_store.hrl: -------------------------------------------------------------------------------- 1 | %% The contents of this file are subject to the Mozilla Public License 2 | %% Version 1.1 (the "License"); you may not use this file except in 3 | %% compliance with the License. You may obtain a copy of the License 4 | %% at http://www.mozilla.org/MPL/ 5 | %% 6 | %% Software distributed under the License is distributed on an "AS IS" 7 | %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 8 | %% the License for the specific language governing rights and 9 | %% limitations under the License. 10 | %% 11 | %% The Original Code is RabbitMQ. 12 | %% 13 | %% The Initial Developer of the Original Code is Pivotal Software, Inc. 14 | %% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. 15 | %% 16 | 17 | -include("rabbit.hrl"). 18 | 19 | -ifdef(use_specs). 20 | 21 | -type(msg() :: any()). 22 | 23 | -endif. 24 | 25 | -record(msg_location, {msg_id, ref_count, file, offset, total_size}). 26 | -------------------------------------------------------------------------------- /rebar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbrisbin/rabbit_common/80814606ae23cc820c74e443383e192cd69ec030/rebar -------------------------------------------------------------------------------- /rebar.config: -------------------------------------------------------------------------------- 1 | {erl_opts, [ 2 | debug_info, 3 | compressed, 4 | report, 5 | warn_export_all, 6 | nowarn_export_vars, 7 | warn_shadow_vars, 8 | warn_unused_function, 9 | warn_obsolete_guard, 10 | warn_unused_import 11 | ]}. 12 | -------------------------------------------------------------------------------- /src/app_utils.erl: -------------------------------------------------------------------------------- 1 | %% The contents of this file are subject to the Mozilla Public License 2 | %% Version 1.1 (the "License"); you may not use this file except in 3 | %% compliance with the License. You may obtain a copy of the License 4 | %% at http://www.mozilla.org/MPL/ 5 | %% 6 | %% Software distributed under the License is distributed on an "AS IS" 7 | %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 8 | %% the License for the specific language governing rights and 9 | %% limitations under the License. 10 | %% 11 | %% The Original Code is RabbitMQ. 12 | %% 13 | %% The Initial Developer of the Original Code is GoPivotal, Inc. 14 | %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved. 15 | %% 16 | -module(app_utils). 17 | 18 | -export([load_applications/1, start_applications/1, start_applications/2, 19 | stop_applications/1, stop_applications/2, app_dependency_order/2, 20 | app_dependencies/1]). 21 | 22 | -ifdef(use_specs). 23 | 24 | -type error_handler() :: fun((atom(), any()) -> 'ok'). 25 | 26 | -spec load_applications([atom()]) -> 'ok'. 27 | -spec start_applications([atom()]) -> 'ok'. 28 | -spec stop_applications([atom()]) -> 'ok'. 29 | -spec start_applications([atom()], error_handler()) -> 'ok'. 30 | -spec stop_applications([atom()], error_handler()) -> 'ok'. 31 | -spec app_dependency_order([atom()], boolean()) -> [digraph:vertex()]. 32 | -spec app_dependencies(atom()) -> [atom()]. 33 | 34 | -endif. 35 | 36 | %%--------------------------------------------------------------------------- 37 | %% Public API 38 | 39 | load_applications(Apps) -> 40 | load_applications(queue:from_list(Apps), sets:new()), 41 | ok. 42 | 43 | start_applications(Apps) -> 44 | start_applications( 45 | Apps, fun (App, Reason) -> 46 | throw({error, {cannot_start_application, App, Reason}}) 47 | end). 48 | 49 | stop_applications(Apps) -> 50 | stop_applications( 51 | Apps, fun (App, Reason) -> 52 | throw({error, {cannot_stop_application, App, Reason}}) 53 | end). 54 | 55 | start_applications(Apps, ErrorHandler) -> 56 | manage_applications(fun lists:foldl/3, 57 | fun application:start/1, 58 | fun application:stop/1, 59 | already_started, 60 | ErrorHandler, 61 | Apps). 62 | 63 | stop_applications(Apps, ErrorHandler) -> 64 | manage_applications(fun lists:foldr/3, 65 | fun application:stop/1, 66 | fun application:start/1, 67 | not_started, 68 | ErrorHandler, 69 | Apps). 70 | 71 | app_dependency_order(RootApps, StripUnreachable) -> 72 | {ok, G} = rabbit_misc:build_acyclic_graph( 73 | fun ({App, _Deps}) -> [{App, App}] end, 74 | fun ({App, Deps}) -> [{Dep, App} || Dep <- Deps] end, 75 | [{App, app_dependencies(App)} || 76 | {App, _Desc, _Vsn} <- application:loaded_applications()]), 77 | try 78 | case StripUnreachable of 79 | true -> digraph:del_vertices(G, digraph:vertices(G) -- 80 | digraph_utils:reachable(RootApps, G)); 81 | false -> ok 82 | end, 83 | digraph_utils:topsort(G) 84 | after 85 | true = digraph:delete(G) 86 | end. 87 | 88 | %%--------------------------------------------------------------------------- 89 | %% Private API 90 | 91 | load_applications(Worklist, Loaded) -> 92 | case queue:out(Worklist) of 93 | {empty, _WorkList} -> 94 | ok; 95 | {{value, App}, Worklist1} -> 96 | case sets:is_element(App, Loaded) of 97 | true -> load_applications(Worklist1, Loaded); 98 | false -> case application:load(App) of 99 | ok -> ok; 100 | {error, {already_loaded, App}} -> ok; 101 | Error -> throw(Error) 102 | end, 103 | load_applications( 104 | queue:join(Worklist1, 105 | queue:from_list(app_dependencies(App))), 106 | sets:add_element(App, Loaded)) 107 | end 108 | end. 109 | 110 | app_dependencies(App) -> 111 | case application:get_key(App, applications) of 112 | undefined -> []; 113 | {ok, Lst} -> Lst 114 | end. 115 | 116 | manage_applications(Iterate, Do, Undo, SkipError, ErrorHandler, Apps) -> 117 | Iterate(fun (App, Acc) -> 118 | case Do(App) of 119 | ok -> [App | Acc]; 120 | {error, {SkipError, _}} -> Acc; 121 | {error, Reason} -> 122 | lists:foreach(Undo, Acc), 123 | ErrorHandler(App, Reason) 124 | end 125 | end, [], Apps), 126 | ok. 127 | 128 | -------------------------------------------------------------------------------- /src/code_version.erl: -------------------------------------------------------------------------------- 1 | %% The contents of this file are subject to the Mozilla Public License 2 | %% Version 1.1 (the "License"); you may not use this file except in 3 | %% compliance with the License. You may obtain a copy of the License 4 | %% at http://www.mozilla.org/MPL/ 5 | %% 6 | %% Software distributed under the License is distributed on an "AS IS" 7 | %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 8 | %% the License for the specific language governing rights and 9 | %% limitations under the License. 10 | %% 11 | %% The Original Code is RabbitMQ Federation. 12 | %% 13 | %% The Initial Developer of the Original Code is GoPivotal, Inc. 14 | %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved. 15 | %% 16 | -module(code_version). 17 | 18 | -export([update/1]). 19 | 20 | %%---------------------------------------------------------------------------- 21 | %% API 22 | %%---------------------------------------------------------------------------- 23 | 24 | %%---------------------------------------------------------------------------- 25 | %% @doc Reads the abstract code of the given `Module`, modifies it to adapt to 26 | %% the current Erlang version, compiles and loads the result. 27 | %% This function finds the current Erlang version and then selects the function 28 | %% call for that version, removing all other versions declared in the original 29 | %% beam file. `code_version:update/1` is triggered by the module itself the 30 | %% first time an affected function is called. 31 | %% 32 | %% The purpose of this functionality is to support the new time API introduced 33 | %% in ERTS 7.0, while providing compatibility with previous versions. 34 | %% 35 | %% `Module` must contain an attribute `erlang_version_support` containing a list of 36 | %% tuples: 37 | %% 38 | %% {ErlangVersion, [{OriginalFuntion, Arity, PreErlangVersionFunction, 39 | %% PostErlangVersionFunction}]} 40 | %% 41 | %% All these new functions may be exported, and implemented as follows: 42 | %% 43 | %% OriginalFunction() -> 44 | %% code_version:update(?MODULE), 45 | %% ?MODULE:OriginalFunction(). 46 | %% 47 | %% PostErlangVersionFunction() -> 48 | %% %% implementation using new time API 49 | %% .. 50 | %% 51 | %% PreErlangVersionFunction() -> 52 | %% %% implementation using fallback solution 53 | %% .. 54 | %% 55 | %% See `time_compat.erl` for an example. 56 | %% 57 | %% end 58 | %%---------------------------------------------------------------------------- 59 | -spec update(atom()) -> ok | no_return(). 60 | update(Module) -> 61 | AbsCode = get_abs_code(Module), 62 | Forms = replace_forms(Module, get_otp_version(), AbsCode), 63 | Code = compile_forms(Forms), 64 | load_code(Module, Code). 65 | 66 | %%---------------------------------------------------------------------------- 67 | %% Internal functions 68 | %%---------------------------------------------------------------------------- 69 | load_code(Module, Code) -> 70 | LockId = {{?MODULE, Module}, self()}, 71 | FakeFilename = "Loaded by rabbit_common", 72 | global:set_lock(LockId, [node()]), 73 | case code:which(Module) of 74 | FakeFilename -> 75 | ok; 76 | _ -> 77 | unload(Module), 78 | case code:load_binary(Module, FakeFilename, Code) of 79 | {module, _} -> ok; 80 | {error, Reason} -> throw({cannot_load, Module, Reason}) 81 | end 82 | end, 83 | global:del_lock(LockId, [node()]), 84 | ok. 85 | 86 | unload(Module) -> 87 | code:soft_purge(Module), 88 | code:delete(Module). 89 | 90 | compile_forms(Forms) -> 91 | case compile:forms(Forms, [debug_info]) of 92 | {ok, _ModName, Code} -> 93 | Code; 94 | {ok, _ModName, Code, _Warnings} -> 95 | Code; 96 | Error -> 97 | throw({cannot_compile_forms, Error}) 98 | end. 99 | 100 | get_abs_code(Module) -> 101 | get_forms(get_object_code(Module)). 102 | 103 | get_object_code(Module) -> 104 | case code:get_object_code(Module) of 105 | {_Mod, Code, _File} -> 106 | Code; 107 | error -> 108 | throw({not_found, Module}) 109 | end. 110 | 111 | get_forms(Code) -> 112 | case beam_lib:chunks(Code, [abstract_code]) of 113 | {ok, {_, [{abstract_code, {raw_abstract_v1, Forms}}]}} -> 114 | Forms; 115 | {ok, {Module, [{abstract_code, no_abstract_code}]}} -> 116 | throw({no_abstract_code, Module}); 117 | {error, beam_lib, Reason} -> 118 | throw({no_abstract_code, Reason}) 119 | end. 120 | 121 | get_otp_version() -> 122 | Version = erlang:system_info(otp_release), 123 | case re:run(Version, "^[0-9][0-9]", [{capture, first, list}]) of 124 | {match, [V]} -> 125 | list_to_integer(V); 126 | _ -> 127 | %% Could be anything below R17, we are not interested 128 | 0 129 | end. 130 | 131 | get_original_pairs(VersionSupport) -> 132 | [{Orig, Arity} || {Orig, Arity, _Pre, _Post} <- VersionSupport]. 133 | 134 | get_delete_pairs(true, VersionSupport) -> 135 | [{Pre, Arity} || {_Orig, Arity, Pre, _Post} <- VersionSupport]; 136 | get_delete_pairs(false, VersionSupport) -> 137 | [{Post, Arity} || {_Orig, Arity, _Pre, Post} <- VersionSupport]. 138 | 139 | get_rename_pairs(true, VersionSupport) -> 140 | [{Post, Arity} || {_Orig, Arity, _Pre, Post} <- VersionSupport]; 141 | get_rename_pairs(false, VersionSupport) -> 142 | [{Pre, Arity} || {_Orig, Arity, Pre, _Post} <- VersionSupport]. 143 | 144 | %% Pairs of {Renamed, OriginalName} functions 145 | get_name_pairs(true, VersionSupport) -> 146 | [{{Post, Arity}, Orig} || {Orig, Arity, _Pre, Post} <- VersionSupport]; 147 | get_name_pairs(false, VersionSupport) -> 148 | [{{Pre, Arity}, Orig} || {Orig, Arity, Pre, _Post} <- VersionSupport]. 149 | 150 | delete_abstract_functions(ToDelete) -> 151 | fun(Tree, Function) -> 152 | case lists:member(Function, ToDelete) of 153 | true -> 154 | erl_syntax:comment(["Deleted unused function"]); 155 | false -> 156 | Tree 157 | end 158 | end. 159 | 160 | rename_abstract_functions(ToRename, ToName) -> 161 | fun(Tree, Function) -> 162 | case lists:member(Function, ToRename) of 163 | true -> 164 | FunctionName = proplists:get_value(Function, ToName), 165 | erl_syntax:function( 166 | erl_syntax:atom(FunctionName), 167 | erl_syntax:function_clauses(Tree)); 168 | false -> 169 | Tree 170 | end 171 | end. 172 | 173 | replace_forms(Module, ErlangVersion, AbsCode) -> 174 | %% Obtain attribute containing the list of functions that must be updated 175 | Attr = Module:module_info(attributes), 176 | VersionSupport = proplists:get_value(erlang_version_support, Attr), 177 | {Pre, Post} = lists:splitwith(fun({Version, _Pairs}) -> 178 | Version > ErlangVersion 179 | end, VersionSupport), 180 | %% Replace functions in two passes: replace for Erlang versions > current 181 | %% first, Erlang versions =< current afterwards. 182 | replace_version_forms( 183 | true, replace_version_forms(false, AbsCode, get_version_functions(Pre)), 184 | get_version_functions(Post)). 185 | 186 | get_version_functions(List) -> 187 | lists:append([Pairs || {_Version, Pairs} <- List]). 188 | 189 | replace_version_forms(IsPost, AbsCode, VersionSupport) -> 190 | %% Get pairs of {Function, Arity} for the triggering functions, which 191 | %% are also the final function names. 192 | Original = get_original_pairs(VersionSupport), 193 | %% Get pairs of {Function, Arity} for the unused version 194 | ToDelete = get_delete_pairs(IsPost, VersionSupport), 195 | %% Delete original functions (those that trigger the code update) and 196 | %% the unused version ones 197 | DeleteFun = delete_abstract_functions(ToDelete ++ Original), 198 | AbsCode0 = replace_function_forms(AbsCode, DeleteFun), 199 | %% Get pairs of {Function, Arity} for the current version which must be 200 | %% renamed 201 | ToRename = get_rename_pairs(IsPost, VersionSupport), 202 | %% Get paris of {Renamed, OriginalName} functions 203 | ToName = get_name_pairs(IsPost, VersionSupport), 204 | %% Rename versioned functions with their final name 205 | RenameFun = rename_abstract_functions(ToRename, ToName), 206 | %% Remove exports of all versioned functions 207 | remove_exports(replace_function_forms(AbsCode0, RenameFun), 208 | ToDelete ++ ToRename). 209 | 210 | replace_function_forms(AbsCode, Fun) -> 211 | ReplaceFunction = 212 | fun(Tree) -> 213 | Function = erl_syntax_lib:analyze_function(Tree), 214 | Fun(Tree, Function) 215 | end, 216 | Filter = fun(Tree) -> 217 | case erl_syntax:type(Tree) of 218 | function -> ReplaceFunction(Tree); 219 | _Other -> Tree 220 | end 221 | end, 222 | fold_syntax_tree(Filter, AbsCode). 223 | 224 | filter_export_pairs(Info, ToDelete) -> 225 | lists:filter(fun(Pair) -> 226 | not lists:member(Pair, ToDelete) 227 | end, Info). 228 | 229 | remove_exports(AbsCode, ToDelete) -> 230 | RemoveExports = 231 | fun(Tree) -> 232 | case erl_syntax_lib:analyze_attribute(Tree) of 233 | {export, Info} -> 234 | Remaining = filter_export_pairs(Info, ToDelete), 235 | rebuild_export(Remaining); 236 | _Other -> Tree 237 | end 238 | end, 239 | Filter = fun(Tree) -> 240 | case erl_syntax:type(Tree) of 241 | attribute -> RemoveExports(Tree); 242 | _Other -> Tree 243 | end 244 | end, 245 | fold_syntax_tree(Filter, AbsCode). 246 | 247 | rebuild_export(Args) -> 248 | erl_syntax:attribute( 249 | erl_syntax:atom(export), 250 | [erl_syntax:list( 251 | [erl_syntax:arity_qualifier(erl_syntax:atom(N), 252 | erl_syntax:integer(A)) 253 | || {N, A} <- Args])]). 254 | 255 | fold_syntax_tree(Filter, Forms) -> 256 | Tree = erl_syntax:form_list(Forms), 257 | NewTree = erl_syntax_lib:map(Filter, Tree), 258 | erl_syntax:revert_forms(NewTree). 259 | -------------------------------------------------------------------------------- /src/credit_flow.erl: -------------------------------------------------------------------------------- 1 | %% The contents of this file are subject to the Mozilla Public License 2 | %% Version 1.1 (the "License"); you may not use this file except in 3 | %% compliance with the License. You may obtain a copy of the License 4 | %% at http://www.mozilla.org/MPL/ 5 | %% 6 | %% Software distributed under the License is distributed on an "AS IS" 7 | %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 8 | %% the License for the specific language governing rights and 9 | %% limitations under the License. 10 | %% 11 | %% The Original Code is RabbitMQ. 12 | %% 13 | %% The Initial Developer of the Original Code is GoPivotal, Inc. 14 | %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved. 15 | %% 16 | 17 | -module(credit_flow). 18 | 19 | %% Credit flow is controlled by a credit specification - a 20 | %% {InitialCredit, MoreCreditAfter} tuple. For the message sender, 21 | %% credit starts at InitialCredit and is decremented with every 22 | %% message sent. The message receiver grants more credit to the sender 23 | %% by sending it a {bump_credit, ...} control message after receiving 24 | %% MoreCreditAfter messages. The sender should pass this message in to 25 | %% handle_bump_msg/1. The sender should block when it goes below 0 26 | %% (check by invoking blocked/0). If a process is both a sender and a 27 | %% receiver it will not grant any more credit to its senders when it 28 | %% is itself blocked - thus the only processes that need to check 29 | %% blocked/0 are ones that read from network sockets. 30 | %% 31 | %% Credit flows left to right when process send messags down the 32 | %% chain, starting at the rabbit_reader, ending at the msg_store: 33 | %% reader -> channel -> queue_process -> msg_store. 34 | %% 35 | %% If the message store has a back log, then it will block the 36 | %% queue_process, which will block the channel, and finally the reader 37 | %% will be blocked, throttling down publishers. 38 | %% 39 | %% Once a process is unblocked, it will grant credits up the chain, 40 | %% possibly unblocking other processes: 41 | %% reader <--grant channel <--grant queue_process <--grant msg_store. 42 | %% 43 | %% Grepping the project files for `credit_flow` will reveal the places 44 | %% where this module is currently used, with extra comments on what's 45 | %% going on at each instance. Note that credit flow between mirrors 46 | %% synchronization has not been documented, since this doesn't affect 47 | %% client publishes. 48 | 49 | -define(DEFAULT_INITIAL_CREDIT, 200). 50 | -define(DEFAULT_MORE_CREDIT_AFTER, 50). 51 | 52 | -define(DEFAULT_CREDIT, 53 | case get(credit_flow_default_credit) of 54 | undefined -> 55 | Val = rabbit_misc:get_env(rabbit, credit_flow_default_credit, 56 | {?DEFAULT_INITIAL_CREDIT, 57 | ?DEFAULT_MORE_CREDIT_AFTER}), 58 | put(credit_flow_default_credit, Val), 59 | Val; 60 | Val -> Val 61 | end). 62 | 63 | -export([send/1, send/2, ack/1, ack/2, handle_bump_msg/1, blocked/0, state/0]). 64 | -export([peer_down/1]). 65 | 66 | %%---------------------------------------------------------------------------- 67 | 68 | -ifdef(use_specs). 69 | 70 | -export_type([bump_msg/0]). 71 | 72 | -opaque(bump_msg() :: {pid(), non_neg_integer()}). 73 | -type(credit_spec() :: {non_neg_integer(), non_neg_integer()}). 74 | 75 | -spec(send/1 :: (pid()) -> 'ok'). 76 | -spec(send/2 :: (pid(), credit_spec()) -> 'ok'). 77 | -spec(ack/1 :: (pid()) -> 'ok'). 78 | -spec(ack/2 :: (pid(), credit_spec()) -> 'ok'). 79 | -spec(handle_bump_msg/1 :: (bump_msg()) -> 'ok'). 80 | -spec(blocked/0 :: () -> boolean()). 81 | -spec(peer_down/1 :: (pid()) -> 'ok'). 82 | 83 | -endif. 84 | 85 | %%---------------------------------------------------------------------------- 86 | 87 | %% process dict update macro - eliminates the performance-hurting 88 | %% closure creation a HOF would introduce 89 | -define(UPDATE(Key, Default, Var, Expr), 90 | begin 91 | %% We deliberately allow Var to escape from the case here 92 | %% to be used in Expr. Any temporary var we introduced 93 | %% would also escape, and might conflict. 94 | Var = case get(Key) of 95 | undefined -> Default; 96 | V -> V 97 | end, 98 | put(Key, Expr) 99 | end). 100 | 101 | %% If current process was blocked by credit flow in the last 102 | %% STATE_CHANGE_INTERVAL milliseconds, state/0 will report it as "in 103 | %% flow". 104 | -define(STATE_CHANGE_INTERVAL, 1000000). 105 | 106 | -ifdef(CREDIT_FLOW_TRACING). 107 | -define(TRACE_BLOCKED(SELF, FROM), rabbit_event:notify(credit_flow_blocked, 108 | [{process, SELF}, 109 | {process_info, erlang:process_info(SELF)}, 110 | {from, FROM}, 111 | {from_info, erlang:process_info(FROM)}, 112 | {timestamp, 113 | time_compat:os_system_time( 114 | milliseconds)}])). 115 | -define(TRACE_UNBLOCKED(SELF, FROM), rabbit_event:notify(credit_flow_unblocked, 116 | [{process, SELF}, 117 | {from, FROM}, 118 | {timestamp, 119 | time_compat:os_system_time( 120 | milliseconds)}])). 121 | -else. 122 | -define(TRACE_BLOCKED(SELF, FROM), ok). 123 | -define(TRACE_UNBLOCKED(SELF, FROM), ok). 124 | -endif. 125 | 126 | %%---------------------------------------------------------------------------- 127 | 128 | %% There are two "flows" here; of messages and of credit, going in 129 | %% opposite directions. The variable names "From" and "To" refer to 130 | %% the flow of credit, but the function names refer to the flow of 131 | %% messages. This is the clearest I can make it (since the function 132 | %% names form the API and want to make sense externally, while the 133 | %% variable names are used in credit bookkeeping and want to make 134 | %% sense internally). 135 | 136 | %% For any given pair of processes, ack/2 and send/2 must always be 137 | %% called with the same credit_spec(). 138 | 139 | send(From) -> send(From, ?DEFAULT_CREDIT). 140 | 141 | send(From, {InitialCredit, _MoreCreditAfter}) -> 142 | ?UPDATE({credit_from, From}, InitialCredit, C, 143 | if C == 1 -> block(From), 144 | 0; 145 | true -> C - 1 146 | end). 147 | 148 | ack(To) -> ack(To, ?DEFAULT_CREDIT). 149 | 150 | ack(To, {_InitialCredit, MoreCreditAfter}) -> 151 | ?UPDATE({credit_to, To}, MoreCreditAfter, C, 152 | if C == 1 -> grant(To, MoreCreditAfter), 153 | MoreCreditAfter; 154 | true -> C - 1 155 | end). 156 | 157 | handle_bump_msg({From, MoreCredit}) -> 158 | ?UPDATE({credit_from, From}, 0, C, 159 | if C =< 0 andalso C + MoreCredit > 0 -> unblock(From), 160 | C + MoreCredit; 161 | true -> C + MoreCredit 162 | end). 163 | 164 | blocked() -> case get(credit_blocked) of 165 | undefined -> false; 166 | [] -> false; 167 | _ -> true 168 | end. 169 | 170 | state() -> case blocked() of 171 | true -> flow; 172 | false -> case get(credit_blocked_at) of 173 | undefined -> running; 174 | B -> Now = time_compat:monotonic_time(), 175 | Diff = time_compat:convert_time_unit(Now - B, 176 | native, 177 | micro_seconds), 178 | case Diff < ?STATE_CHANGE_INTERVAL of 179 | true -> flow; 180 | false -> running 181 | end 182 | end 183 | end. 184 | 185 | peer_down(Peer) -> 186 | %% In theory we could also remove it from credit_deferred here, but it 187 | %% doesn't really matter; at some point later we will drain 188 | %% credit_deferred and thus send messages into the void... 189 | unblock(Peer), 190 | erase({credit_from, Peer}), 191 | erase({credit_to, Peer}), 192 | ok. 193 | 194 | %% -------------------------------------------------------------------------- 195 | 196 | grant(To, Quantity) -> 197 | Msg = {bump_credit, {self(), Quantity}}, 198 | case blocked() of 199 | false -> To ! Msg; 200 | true -> ?UPDATE(credit_deferred, [], Deferred, [{To, Msg} | Deferred]) 201 | end. 202 | 203 | block(From) -> 204 | ?TRACE_BLOCKED(self(), From), 205 | case blocked() of 206 | false -> put(credit_blocked_at, time_compat:monotonic_time()); 207 | true -> ok 208 | end, 209 | ?UPDATE(credit_blocked, [], Blocks, [From | Blocks]). 210 | 211 | unblock(From) -> 212 | ?TRACE_UNBLOCKED(self(), From), 213 | ?UPDATE(credit_blocked, [], Blocks, Blocks -- [From]), 214 | case blocked() of 215 | false -> case erase(credit_deferred) of 216 | undefined -> ok; 217 | Credits -> _ = [To ! Msg || {To, Msg} <- Credits], 218 | ok 219 | end; 220 | true -> ok 221 | end. 222 | -------------------------------------------------------------------------------- /src/pmon.erl: -------------------------------------------------------------------------------- 1 | %% The contents of this file are subject to the Mozilla Public License 2 | %% Version 1.1 (the "License"); you may not use this file except in 3 | %% compliance with the License. You may obtain a copy of the License 4 | %% at http://www.mozilla.org/MPL/ 5 | %% 6 | %% Software distributed under the License is distributed on an "AS IS" 7 | %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 8 | %% the License for the specific language governing rights and 9 | %% limitations under the License. 10 | %% 11 | %% The Original Code is RabbitMQ. 12 | %% 13 | %% The Initial Developer of the Original Code is GoPivotal, Inc. 14 | %% Copyright (c) 2011-2015 Pivotal Software, Inc. All rights reserved. 15 | %% 16 | 17 | -module(pmon). 18 | 19 | %% Process Monitor 20 | %% ================ 21 | %% 22 | %% This module monitors processes so that every process has at most 23 | %% 1 monitor. 24 | %% Processes monitored can be dynamically added and removed. 25 | %% 26 | %% Unlike erlang:[de]monitor* functions, this module 27 | %% provides basic querying capability and avoids contacting down nodes. 28 | %% 29 | %% It is used to monitor nodes, queue mirrors, and by 30 | %% the queue collector, among other things. 31 | 32 | -export([new/0, new/1, monitor/2, monitor_all/2, demonitor/2, 33 | is_monitored/2, erase/2, monitored/1, is_empty/1]). 34 | 35 | -compile({no_auto_import, [monitor/2]}). 36 | 37 | -record(state, {dict, module}). 38 | 39 | -ifdef(use_specs). 40 | 41 | %%---------------------------------------------------------------------------- 42 | 43 | -export_type([?MODULE/0]). 44 | 45 | -opaque(?MODULE() :: #state{dict :: dict:dict(), 46 | module :: atom()}). 47 | 48 | -type(item() :: pid() | {atom(), node()}). 49 | 50 | -spec(new/0 :: () -> ?MODULE()). 51 | -spec(new/1 :: ('erlang' | 'delegate') -> ?MODULE()). 52 | -spec(monitor/2 :: (item(), ?MODULE()) -> ?MODULE()). 53 | -spec(monitor_all/2 :: ([item()], ?MODULE()) -> ?MODULE()). 54 | -spec(demonitor/2 :: (item(), ?MODULE()) -> ?MODULE()). 55 | -spec(is_monitored/2 :: (item(), ?MODULE()) -> boolean()). 56 | -spec(erase/2 :: (item(), ?MODULE()) -> ?MODULE()). 57 | -spec(monitored/1 :: (?MODULE()) -> [item()]). 58 | -spec(is_empty/1 :: (?MODULE()) -> boolean()). 59 | 60 | -endif. 61 | 62 | new() -> new(erlang). 63 | 64 | new(Module) -> #state{dict = dict:new(), 65 | module = Module}. 66 | 67 | monitor(Item, S = #state{dict = M, module = Module}) -> 68 | case dict:is_key(Item, M) of 69 | true -> S; 70 | false -> case node_alive_shortcut(Item) of 71 | true -> Ref = Module:monitor(process, Item), 72 | S#state{dict = dict:store(Item, Ref, M)}; 73 | false -> self() ! {'DOWN', fake_ref, process, Item, 74 | nodedown}, 75 | S 76 | end 77 | end. 78 | 79 | monitor_all([], S) -> S; %% optimisation 80 | monitor_all([Item], S) -> monitor(Item, S); %% optimisation 81 | monitor_all(Items, S) -> lists:foldl(fun monitor/2, S, Items). 82 | 83 | demonitor(Item, S = #state{dict = M, module = Module}) -> 84 | case dict:find(Item, M) of 85 | {ok, MRef} -> Module:demonitor(MRef), 86 | S#state{dict = dict:erase(Item, M)}; 87 | error -> S 88 | end. 89 | 90 | is_monitored(Item, #state{dict = M}) -> dict:is_key(Item, M). 91 | 92 | erase(Item, S = #state{dict = M}) -> S#state{dict = dict:erase(Item, M)}. 93 | 94 | monitored(#state{dict = M}) -> dict:fetch_keys(M). 95 | 96 | is_empty(#state{dict = M}) -> dict:size(M) == 0. 97 | 98 | %%---------------------------------------------------------------------------- 99 | 100 | %% We check here to see if the node is alive in order to avoid trying 101 | %% to connect to it if it isn't - this can cause substantial 102 | %% slowdowns. We can't perform this shortcut if passed {Name, Node} 103 | %% since we would need to convert that into a pid for the fake 'DOWN' 104 | %% message, so we always return true here - but that's OK, it's just 105 | %% an optimisation. 106 | node_alive_shortcut(P) when is_pid(P) -> 107 | lists:member(node(P), [node() | nodes()]); 108 | node_alive_shortcut({_Name, _Node}) -> 109 | true. 110 | -------------------------------------------------------------------------------- /src/priority_queue.erl: -------------------------------------------------------------------------------- 1 | %% The contents of this file are subject to the Mozilla Public License 2 | %% Version 1.1 (the "License"); you may not use this file except in 3 | %% compliance with the License. You may obtain a copy of the License 4 | %% at http://www.mozilla.org/MPL/ 5 | %% 6 | %% Software distributed under the License is distributed on an "AS IS" 7 | %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 8 | %% the License for the specific language governing rights and 9 | %% limitations under the License. 10 | %% 11 | %% The Original Code is RabbitMQ. 12 | %% 13 | %% The Initial Developer of the Original Code is GoPivotal, Inc. 14 | %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved. 15 | %% 16 | 17 | %% Priority queues have essentially the same interface as ordinary 18 | %% queues, except that a) there is an in/3 that takes a priority, and 19 | %% b) we have only implemented the core API we need. 20 | %% 21 | %% Priorities should be integers - the higher the value the higher the 22 | %% priority - but we don't actually check that. 23 | %% 24 | %% in/2 inserts items with priority 0. 25 | %% 26 | %% We optimise the case where a priority queue is being used just like 27 | %% an ordinary queue. When that is the case we represent the priority 28 | %% queue as an ordinary queue. We could just call into the 'queue' 29 | %% module for that, but for efficiency we implement the relevant 30 | %% functions directly in here, thus saving on inter-module calls and 31 | %% eliminating a level of boxing. 32 | %% 33 | %% When the queue contains items with non-zero priorities, it is 34 | %% represented as a sorted kv list with the inverted Priority as the 35 | %% key and an ordinary queue as the value. Here again we use our own 36 | %% ordinary queue implemention for efficiency, often making recursive 37 | %% calls into the same function knowing that ordinary queues represent 38 | %% a base case. 39 | 40 | 41 | -module(priority_queue). 42 | 43 | -export([new/0, is_queue/1, is_empty/1, len/1, to_list/1, from_list/1, 44 | in/2, in/3, out/1, out_p/1, join/2, filter/2, fold/3, highest/1]). 45 | 46 | %%---------------------------------------------------------------------------- 47 | 48 | -ifdef(use_specs). 49 | 50 | -export_type([q/0]). 51 | 52 | -type(q() :: pqueue()). 53 | -type(priority() :: integer() | 'infinity'). 54 | -type(squeue() :: {queue, [any()], [any()], non_neg_integer()}). 55 | -type(pqueue() :: squeue() | {pqueue, [{priority(), squeue()}]}). 56 | 57 | -spec(new/0 :: () -> pqueue()). 58 | -spec(is_queue/1 :: (any()) -> boolean()). 59 | -spec(is_empty/1 :: (pqueue()) -> boolean()). 60 | -spec(len/1 :: (pqueue()) -> non_neg_integer()). 61 | -spec(to_list/1 :: (pqueue()) -> [{priority(), any()}]). 62 | -spec(from_list/1 :: ([{priority(), any()}]) -> pqueue()). 63 | -spec(in/2 :: (any(), pqueue()) -> pqueue()). 64 | -spec(in/3 :: (any(), priority(), pqueue()) -> pqueue()). 65 | -spec(out/1 :: (pqueue()) -> {empty | {value, any()}, pqueue()}). 66 | -spec(out_p/1 :: (pqueue()) -> {empty | {value, any(), priority()}, pqueue()}). 67 | -spec(join/2 :: (pqueue(), pqueue()) -> pqueue()). 68 | -spec(filter/2 :: (fun ((any()) -> boolean()), pqueue()) -> pqueue()). 69 | -spec(fold/3 :: 70 | (fun ((any(), priority(), A) -> A), A, pqueue()) -> A). 71 | -spec(highest/1 :: (pqueue()) -> priority() | 'empty'). 72 | 73 | -endif. 74 | 75 | %%---------------------------------------------------------------------------- 76 | 77 | new() -> 78 | {queue, [], [], 0}. 79 | 80 | is_queue({queue, R, F, L}) when is_list(R), is_list(F), is_integer(L) -> 81 | true; 82 | is_queue({pqueue, Queues}) when is_list(Queues) -> 83 | lists:all(fun ({infinity, Q}) -> is_queue(Q); 84 | ({P, Q}) -> is_integer(P) andalso is_queue(Q) 85 | end, Queues); 86 | is_queue(_) -> 87 | false. 88 | 89 | is_empty({queue, [], [], 0}) -> 90 | true; 91 | is_empty(_) -> 92 | false. 93 | 94 | len({queue, _R, _F, L}) -> 95 | L; 96 | len({pqueue, Queues}) -> 97 | lists:sum([len(Q) || {_, Q} <- Queues]). 98 | 99 | to_list({queue, In, Out, _Len}) when is_list(In), is_list(Out) -> 100 | [{0, V} || V <- Out ++ lists:reverse(In, [])]; 101 | to_list({pqueue, Queues}) -> 102 | [{maybe_negate_priority(P), V} || {P, Q} <- Queues, 103 | {0, V} <- to_list(Q)]. 104 | 105 | from_list(L) -> 106 | lists:foldl(fun ({P, E}, Q) -> in(E, P, Q) end, new(), L). 107 | 108 | in(Item, Q) -> 109 | in(Item, 0, Q). 110 | 111 | in(X, 0, {queue, [_] = In, [], 1}) -> 112 | {queue, [X], In, 2}; 113 | in(X, 0, {queue, In, Out, Len}) when is_list(In), is_list(Out) -> 114 | {queue, [X|In], Out, Len + 1}; 115 | in(X, Priority, _Q = {queue, [], [], 0}) -> 116 | in(X, Priority, {pqueue, []}); 117 | in(X, Priority, Q = {queue, _, _, _}) -> 118 | in(X, Priority, {pqueue, [{0, Q}]}); 119 | in(X, Priority, {pqueue, Queues}) -> 120 | P = maybe_negate_priority(Priority), 121 | {pqueue, case lists:keysearch(P, 1, Queues) of 122 | {value, {_, Q}} -> 123 | lists:keyreplace(P, 1, Queues, {P, in(X, Q)}); 124 | false when P == infinity -> 125 | [{P, {queue, [X], [], 1}} | Queues]; 126 | false -> 127 | case Queues of 128 | [{infinity, InfQueue} | Queues1] -> 129 | [{infinity, InfQueue} | 130 | lists:keysort(1, [{P, {queue, [X], [], 1}} | Queues1])]; 131 | _ -> 132 | lists:keysort(1, [{P, {queue, [X], [], 1}} | Queues]) 133 | end 134 | end}. 135 | 136 | out({queue, [], [], 0} = Q) -> 137 | {empty, Q}; 138 | out({queue, [V], [], 1}) -> 139 | {{value, V}, {queue, [], [], 0}}; 140 | out({queue, [Y|In], [], Len}) -> 141 | [V|Out] = lists:reverse(In, []), 142 | {{value, V}, {queue, [Y], Out, Len - 1}}; 143 | out({queue, In, [V], Len}) when is_list(In) -> 144 | {{value,V}, r2f(In, Len - 1)}; 145 | out({queue, In,[V|Out], Len}) when is_list(In) -> 146 | {{value, V}, {queue, In, Out, Len - 1}}; 147 | out({pqueue, [{P, Q} | Queues]}) -> 148 | {R, Q1} = out(Q), 149 | NewQ = case is_empty(Q1) of 150 | true -> case Queues of 151 | [] -> {queue, [], [], 0}; 152 | [{0, OnlyQ}] -> OnlyQ; 153 | [_|_] -> {pqueue, Queues} 154 | end; 155 | false -> {pqueue, [{P, Q1} | Queues]} 156 | end, 157 | {R, NewQ}. 158 | 159 | out_p({queue, _, _, _} = Q) -> add_p(out(Q), 0); 160 | out_p({pqueue, [{P, _} | _]} = Q) -> add_p(out(Q), maybe_negate_priority(P)). 161 | 162 | add_p(R, P) -> case R of 163 | {empty, Q} -> {empty, Q}; 164 | {{value, V}, Q} -> {{value, V, P}, Q} 165 | end. 166 | 167 | join(A, {queue, [], [], 0}) -> 168 | A; 169 | join({queue, [], [], 0}, B) -> 170 | B; 171 | join({queue, AIn, AOut, ALen}, {queue, BIn, BOut, BLen}) -> 172 | {queue, BIn, AOut ++ lists:reverse(AIn, BOut), ALen + BLen}; 173 | join(A = {queue, _, _, _}, {pqueue, BPQ}) -> 174 | {Pre, Post} = 175 | lists:splitwith(fun ({P, _}) -> P < 0 orelse P == infinity end, BPQ), 176 | Post1 = case Post of 177 | [] -> [ {0, A} ]; 178 | [ {0, ZeroQueue} | Rest ] -> [ {0, join(A, ZeroQueue)} | Rest ]; 179 | _ -> [ {0, A} | Post ] 180 | end, 181 | {pqueue, Pre ++ Post1}; 182 | join({pqueue, APQ}, B = {queue, _, _, _}) -> 183 | {Pre, Post} = 184 | lists:splitwith(fun ({P, _}) -> P < 0 orelse P == infinity end, APQ), 185 | Post1 = case Post of 186 | [] -> [ {0, B} ]; 187 | [ {0, ZeroQueue} | Rest ] -> [ {0, join(ZeroQueue, B)} | Rest ]; 188 | _ -> [ {0, B} | Post ] 189 | end, 190 | {pqueue, Pre ++ Post1}; 191 | join({pqueue, APQ}, {pqueue, BPQ}) -> 192 | {pqueue, merge(APQ, BPQ, [])}. 193 | 194 | merge([], BPQ, Acc) -> 195 | lists:reverse(Acc, BPQ); 196 | merge(APQ, [], Acc) -> 197 | lists:reverse(Acc, APQ); 198 | merge([{P, A}|As], [{P, B}|Bs], Acc) -> 199 | merge(As, Bs, [ {P, join(A, B)} | Acc ]); 200 | merge([{PA, A}|As], Bs = [{PB, _}|_], Acc) when PA < PB orelse PA == infinity -> 201 | merge(As, Bs, [ {PA, A} | Acc ]); 202 | merge(As = [{_, _}|_], [{PB, B}|Bs], Acc) -> 203 | merge(As, Bs, [ {PB, B} | Acc ]). 204 | 205 | filter(Pred, Q) -> fold(fun(V, P, Acc) -> 206 | case Pred(V) of 207 | true -> in(V, P, Acc); 208 | false -> Acc 209 | end 210 | end, new(), Q). 211 | 212 | fold(Fun, Init, Q) -> case out_p(Q) of 213 | {empty, _Q} -> Init; 214 | {{value, V, P}, Q1} -> fold(Fun, Fun(V, P, Init), Q1) 215 | end. 216 | 217 | highest({queue, [], [], 0}) -> empty; 218 | highest({queue, _, _, _}) -> 0; 219 | highest({pqueue, [{P, _} | _]}) -> maybe_negate_priority(P). 220 | 221 | r2f([], 0) -> {queue, [], [], 0}; 222 | r2f([_] = R, 1) -> {queue, [], R, 1}; 223 | r2f([X,Y], 2) -> {queue, [X], [Y], 2}; 224 | r2f([X,Y|R], L) -> {queue, [X,Y], lists:reverse(R, []), L}. 225 | 226 | maybe_negate_priority(infinity) -> infinity; 227 | maybe_negate_priority(P) -> -P. 228 | -------------------------------------------------------------------------------- /src/rabbit_auth_backend_dummy.erl: -------------------------------------------------------------------------------- 1 | %% The contents of this file are subject to the Mozilla Public License 2 | %% Version 1.1 (the "License"); you may not use this file except in 3 | %% compliance with the License. You may obtain a copy of the License 4 | %% at http://www.mozilla.org/MPL/ 5 | %% 6 | %% Software distributed under the License is distributed on an "AS IS" 7 | %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 8 | %% the License for the specific language governing rights and 9 | %% limitations under the License. 10 | %% 11 | %% The Original Code is RabbitMQ. 12 | %% 13 | %% The Initial Developer of the Original Code is GoPivotal, Inc. 14 | %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved. 15 | %% 16 | 17 | -module(rabbit_auth_backend_dummy). 18 | -include("rabbit.hrl"). 19 | 20 | -behaviour(rabbit_authn_backend). 21 | -behaviour(rabbit_authz_backend). 22 | 23 | -export([user/0]). 24 | -export([user_login_authentication/2, user_login_authorization/1, 25 | check_vhost_access/3, check_resource_access/3]). 26 | 27 | -ifdef(use_specs). 28 | 29 | -spec(user/0 :: () -> rabbit_types:user()). 30 | 31 | -endif. 32 | 33 | %% A user to be used by the direct client when permission checks are 34 | %% not needed. This user can do anything AMQPish. 35 | user() -> #user{username = <<"none">>, 36 | tags = [], 37 | authz_backends = [{?MODULE, none}]}. 38 | 39 | %% Implementation of rabbit_auth_backend 40 | 41 | user_login_authentication(_, _) -> 42 | {refused, "cannot log in conventionally as dummy user", []}. 43 | 44 | user_login_authorization(_) -> 45 | {refused, "cannot log in conventionally as dummy user", []}. 46 | 47 | check_vhost_access(#auth_user{}, _VHostPath, _Sock) -> true. 48 | check_resource_access(#auth_user{}, #resource{}, _Permission) -> true. 49 | -------------------------------------------------------------------------------- /src/rabbit_auth_mechanism.erl: -------------------------------------------------------------------------------- 1 | %% The contents of this file are subject to the Mozilla Public License 2 | %% Version 1.1 (the "License"); you may not use this file except in 3 | %% compliance with the License. You may obtain a copy of the License 4 | %% at http://www.mozilla.org/MPL/ 5 | %% 6 | %% Software distributed under the License is distributed on an "AS IS" 7 | %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 8 | %% the License for the specific language governing rights and 9 | %% limitations under the License. 10 | %% 11 | %% The Original Code is RabbitMQ. 12 | %% 13 | %% The Initial Developer of the Original Code is GoPivotal, Inc. 14 | %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved. 15 | %% 16 | 17 | -module(rabbit_auth_mechanism). 18 | 19 | -ifdef(use_specs). 20 | 21 | %% A description. 22 | -callback description() -> [proplists:property()]. 23 | 24 | %% If this mechanism is enabled, should it be offered for a given socket? 25 | %% (primarily so EXTERNAL can be SSL-only) 26 | -callback should_offer(rabbit_net:socket()) -> boolean(). 27 | 28 | %% Called before authentication starts. Should create a state 29 | %% object to be passed through all the stages of authentication. 30 | -callback init(rabbit_net:socket()) -> any(). 31 | 32 | %% Handle a stage of authentication. Possible responses: 33 | %% {ok, User} 34 | %% Authentication succeeded, and here's the user record. 35 | %% {challenge, Challenge, NextState} 36 | %% Another round is needed. Here's the state I want next time. 37 | %% {protocol_error, Msg, Args} 38 | %% Client got the protocol wrong. Log and die. 39 | %% {refused, Username, Msg, Args} 40 | %% Client failed authentication. Log and die. 41 | -callback handle_response(binary(), any()) -> 42 | {'ok', rabbit_types:user()} | 43 | {'challenge', binary(), any()} | 44 | {'protocol_error', string(), [any()]} | 45 | {'refused', rabbit_types:username() | none, string(), [any()]}. 46 | 47 | -else. 48 | 49 | -export([behaviour_info/1]). 50 | 51 | behaviour_info(callbacks) -> 52 | [{description, 0}, {should_offer, 1}, {init, 1}, {handle_response, 2}]; 53 | behaviour_info(_Other) -> 54 | undefined. 55 | 56 | -endif. 57 | -------------------------------------------------------------------------------- /src/rabbit_authn_backend.erl: -------------------------------------------------------------------------------- 1 | %% The contents of this file are subject to the Mozilla Public License 2 | %% Version 1.1 (the "License"); you may not use this file except in 3 | %% compliance with the License. You may obtain a copy of the License 4 | %% at http://www.mozilla.org/MPL/ 5 | %% 6 | %% Software distributed under the License is distributed on an "AS IS" 7 | %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 8 | %% the License for the specific language governing rights and 9 | %% limitations under the License. 10 | %% 11 | %% The Original Code is RabbitMQ. 12 | %% 13 | %% The Initial Developer of the Original Code is GoPivotal, Inc. 14 | %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved. 15 | %% 16 | 17 | -module(rabbit_authn_backend). 18 | 19 | -include("rabbit.hrl"). 20 | 21 | -ifdef(use_specs). 22 | 23 | %% Check a user can log in, given a username and a proplist of 24 | %% authentication information (e.g. [{password, Password}]). If your 25 | %% backend is not to be used for authentication, this should always 26 | %% refuse access. 27 | %% 28 | %% Possible responses: 29 | %% {ok, User} 30 | %% Authentication succeeded, and here's the user record. 31 | %% {error, Error} 32 | %% Something went wrong. Log and die. 33 | %% {refused, Msg, Args} 34 | %% Client failed authentication. Log and die. 35 | -callback user_login_authentication(rabbit_types:username(), [term()]) -> 36 | {'ok', rabbit_types:auth_user()} | 37 | {'refused', string(), [any()]} | 38 | {'error', any()}. 39 | 40 | -else. 41 | 42 | -export([behaviour_info/1]). 43 | 44 | behaviour_info(callbacks) -> 45 | [{user_login_authentication, 2}]; 46 | behaviour_info(_Other) -> 47 | undefined. 48 | 49 | -endif. 50 | -------------------------------------------------------------------------------- /src/rabbit_authz_backend.erl: -------------------------------------------------------------------------------- 1 | %% The contents of this file are subject to the Mozilla Public License 2 | %% Version 1.1 (the "License"); you may not use this file except in 3 | %% compliance with the License. You may obtain a copy of the License 4 | %% at http://www.mozilla.org/MPL/ 5 | %% 6 | %% Software distributed under the License is distributed on an "AS IS" 7 | %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 8 | %% the License for the specific language governing rights and 9 | %% limitations under the License. 10 | %% 11 | %% The Original Code is RabbitMQ. 12 | %% 13 | %% The Initial Developer of the Original Code is GoPivotal, Inc. 14 | %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved. 15 | %% 16 | 17 | -module(rabbit_authz_backend). 18 | 19 | -include("rabbit.hrl"). 20 | 21 | -ifdef(use_specs). 22 | 23 | %% Check a user can log in, when this backend is being used for 24 | %% authorisation only. Authentication has already taken place 25 | %% successfully, but we need to check that the user exists in this 26 | %% backend, and initialise any impl field we will want to have passed 27 | %% back in future calls to check_vhost_access/3 and 28 | %% check_resource_access/3. 29 | %% 30 | %% Possible responses: 31 | %% {ok, Impl} 32 | %% {ok, Impl, Tags} 33 | %% User authorisation succeeded, and here's the impl and potential extra tags fields. 34 | %% {error, Error} 35 | %% Something went wrong. Log and die. 36 | %% {refused, Msg, Args} 37 | %% User authorisation failed. Log and die. 38 | -callback user_login_authorization(rabbit_types:username()) -> 39 | {'ok', any()} | 40 | {'ok', any(), any()} | 41 | {'refused', string(), [any()]} | 42 | {'error', any()}. 43 | 44 | %% Given #auth_user and vhost, can a user log in to a vhost? 45 | %% Possible responses: 46 | %% true 47 | %% false 48 | %% {error, Error} 49 | %% Something went wrong. Log and die. 50 | -callback check_vhost_access(rabbit_types:auth_user(), 51 | rabbit_types:vhost(), rabbit_net:socket()) -> 52 | boolean() | {'error', any()}. 53 | 54 | %% Given #auth_user, resource and permission, can a user access a resource? 55 | %% 56 | %% Possible responses: 57 | %% true 58 | %% false 59 | %% {error, Error} 60 | %% Something went wrong. Log and die. 61 | -callback check_resource_access(rabbit_types:auth_user(), 62 | rabbit_types:r(atom()), 63 | rabbit_access_control:permission_atom()) -> 64 | boolean() | {'error', any()}. 65 | 66 | -else. 67 | 68 | -export([behaviour_info/1]). 69 | 70 | behaviour_info(callbacks) -> 71 | [{user_login_authorization, 1}, 72 | {check_vhost_access, 3}, {check_resource_access, 3}]; 73 | behaviour_info(_Other) -> 74 | undefined. 75 | 76 | -endif. 77 | -------------------------------------------------------------------------------- /src/rabbit_backing_queue.erl: -------------------------------------------------------------------------------- 1 | %% The contents of this file are subject to the Mozilla Public License 2 | %% Version 1.1 (the "License"); you may not use this file except in 3 | %% compliance with the License. You may obtain a copy of the License 4 | %% at http://www.mozilla.org/MPL/ 5 | %% 6 | %% Software distributed under the License is distributed on an "AS IS" 7 | %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 8 | %% the License for the specific language governing rights and 9 | %% limitations under the License. 10 | %% 11 | %% The Original Code is RabbitMQ. 12 | %% 13 | %% The Initial Developer of the Original Code is GoPivotal, Inc. 14 | %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved. 15 | %% 16 | 17 | -module(rabbit_backing_queue). 18 | 19 | -export([info_keys/0]). 20 | 21 | -define(INFO_KEYS, [messages_ram, messages_ready_ram, 22 | messages_unacknowledged_ram, messages_persistent, 23 | message_bytes, message_bytes_ready, 24 | message_bytes_unacknowledged, message_bytes_ram, 25 | message_bytes_persistent, head_message_timestamp, 26 | disk_reads, disk_writes, backing_queue_status]). 27 | 28 | -ifdef(use_specs). 29 | 30 | %% We can't specify a per-queue ack/state with callback signatures 31 | -type(ack() :: any()). 32 | -type(state() :: any()). 33 | 34 | -type(flow() :: 'flow' | 'noflow'). 35 | -type(msg_ids() :: [rabbit_types:msg_id()]). 36 | -type(publish() :: {rabbit_types:basic_message(), 37 | rabbit_types:message_properties(), boolean()}). 38 | -type(delivered_publish() :: {rabbit_types:basic_message(), 39 | rabbit_types:message_properties()}). 40 | -type(fetch_result(Ack) :: 41 | ('empty' | {rabbit_types:basic_message(), boolean(), Ack})). 42 | -type(drop_result(Ack) :: 43 | ('empty' | {rabbit_types:msg_id(), Ack})). 44 | -type(recovery_terms() :: [term()] | 'non_clean_shutdown'). 45 | -type(recovery_info() :: 'new' | recovery_terms()). 46 | -type(purged_msg_count() :: non_neg_integer()). 47 | -type(async_callback() :: 48 | fun ((atom(), fun ((atom(), state()) -> state())) -> 'ok')). 49 | -type(duration() :: ('undefined' | 'infinity' | number())). 50 | 51 | -type(msg_fun(A) :: fun ((rabbit_types:basic_message(), ack(), A) -> A)). 52 | -type(msg_pred() :: fun ((rabbit_types:message_properties()) -> boolean())). 53 | 54 | -type(queue_mode() :: atom()). 55 | 56 | -spec(info_keys/0 :: () -> rabbit_types:info_keys()). 57 | 58 | %% Called on startup with a list of durable queue names. The queues 59 | %% aren't being started at this point, but this call allows the 60 | %% backing queue to perform any checking necessary for the consistency 61 | %% of those queues, or initialise any other shared resources. 62 | %% 63 | %% The list of queue recovery terms returned as {ok, Terms} must be given 64 | %% in the same order as the list of queue names supplied. 65 | -callback start([rabbit_amqqueue:name()]) -> rabbit_types:ok(recovery_terms()). 66 | 67 | %% Called to tear down any state/resources. NB: Implementations should 68 | %% not depend on this function being called on shutdown and instead 69 | %% should hook into the rabbit supervision hierarchy. 70 | -callback stop() -> 'ok'. 71 | 72 | %% Initialise the backing queue and its state. 73 | %% 74 | %% Takes 75 | %% 1. the amqqueue record 76 | %% 2. a term indicating whether the queue is an existing queue that 77 | %% should be recovered or not. When 'new' is given, no recovery is 78 | %% taking place, otherwise a list of recovery terms is given, or 79 | %% the atom 'non_clean_shutdown' if no recovery terms are available. 80 | %% 3. an asynchronous callback which accepts a function of type 81 | %% backing-queue-state to backing-queue-state. This callback 82 | %% function can be safely invoked from any process, which makes it 83 | %% useful for passing messages back into the backing queue, 84 | %% especially as the backing queue does not have control of its own 85 | %% mailbox. 86 | -callback init(rabbit_types:amqqueue(), recovery_info(), 87 | async_callback()) -> state(). 88 | 89 | %% Called on queue shutdown when queue isn't being deleted. 90 | -callback terminate(any(), state()) -> state(). 91 | 92 | %% Called when the queue is terminating and needs to delete all its 93 | %% content. 94 | -callback delete_and_terminate(any(), state()) -> state(). 95 | 96 | %% Called to clean up after a crashed queue. In this case we don't 97 | %% have a process and thus a state(), we are just removing on-disk data. 98 | -callback delete_crashed(rabbit_types:amqqueue()) -> 'ok'. 99 | 100 | %% Remove all 'fetchable' messages from the queue, i.e. all messages 101 | %% except those that have been fetched already and are pending acks. 102 | -callback purge(state()) -> {purged_msg_count(), state()}. 103 | 104 | %% Remove all messages in the queue which have been fetched and are 105 | %% pending acks. 106 | -callback purge_acks(state()) -> state(). 107 | 108 | %% Publish a message. 109 | -callback publish(rabbit_types:basic_message(), 110 | rabbit_types:message_properties(), boolean(), pid(), flow(), 111 | state()) -> state(). 112 | 113 | %% Like publish/6 but for batches of publishes. 114 | -callback batch_publish([publish()], pid(), flow(), state()) -> state(). 115 | 116 | %% Called for messages which have already been passed straight 117 | %% out to a client. The queue will be empty for these calls 118 | %% (i.e. saves the round trip through the backing queue). 119 | -callback publish_delivered(rabbit_types:basic_message(), 120 | rabbit_types:message_properties(), pid(), flow(), 121 | state()) 122 | -> {ack(), state()}. 123 | 124 | %% Like publish_delivered/5 but for batches of publishes. 125 | -callback batch_publish_delivered([delivered_publish()], pid(), flow(), 126 | state()) 127 | -> {[ack()], state()}. 128 | 129 | %% Called to inform the BQ about messages which have reached the 130 | %% queue, but are not going to be further passed to BQ. 131 | -callback discard(rabbit_types:msg_id(), pid(), flow(), state()) -> state(). 132 | 133 | %% Return ids of messages which have been confirmed since the last 134 | %% invocation of this function (or initialisation). 135 | %% 136 | %% Message ids should only appear in the result of drain_confirmed 137 | %% under the following circumstances: 138 | %% 139 | %% 1. The message appears in a call to publish_delivered/4 and the 140 | %% first argument (ack_required) is false; or 141 | %% 2. The message is fetched from the queue with fetch/2 and the first 142 | %% argument (ack_required) is false; or 143 | %% 3. The message is acked (ack/2 is called for the message); or 144 | %% 4. The message is fully fsync'd to disk in such a way that the 145 | %% recovery of the message is guaranteed in the event of a crash of 146 | %% this rabbit node (excluding hardware failure). 147 | %% 148 | %% In addition to the above conditions, a message id may only appear 149 | %% in the result of drain_confirmed if 150 | %% #message_properties.needs_confirming = true when the msg was 151 | %% published (through whichever means) to the backing queue. 152 | %% 153 | %% It is legal for the same message id to appear in the results of 154 | %% multiple calls to drain_confirmed, which means that the backing 155 | %% queue is not required to keep track of which messages it has 156 | %% already confirmed. The confirm will be issued to the publisher the 157 | %% first time the message id appears in the result of 158 | %% drain_confirmed. All subsequent appearances of that message id will 159 | %% be ignored. 160 | -callback drain_confirmed(state()) -> {msg_ids(), state()}. 161 | 162 | %% Drop messages from the head of the queue while the supplied 163 | %% predicate on message properties returns true. Returns the first 164 | %% message properties for which the predictate returned false, or 165 | %% 'undefined' if the whole backing queue was traversed w/o the 166 | %% predicate ever returning false. 167 | -callback dropwhile(msg_pred(), state()) 168 | -> {rabbit_types:message_properties() | undefined, state()}. 169 | 170 | %% Like dropwhile, except messages are fetched in "require 171 | %% acknowledgement" mode and are passed, together with their ack tag, 172 | %% to the supplied function. The function is also fed an 173 | %% accumulator. The result of fetchwhile is as for dropwhile plus the 174 | %% accumulator. 175 | -callback fetchwhile(msg_pred(), msg_fun(A), A, state()) 176 | -> {rabbit_types:message_properties() | undefined, 177 | A, state()}. 178 | 179 | %% Produce the next message. 180 | -callback fetch(true, state()) -> {fetch_result(ack()), state()}; 181 | (false, state()) -> {fetch_result(undefined), state()}. 182 | 183 | %% Remove the next message. 184 | -callback drop(true, state()) -> {drop_result(ack()), state()}; 185 | (false, state()) -> {drop_result(undefined), state()}. 186 | 187 | %% Acktags supplied are for messages which can now be forgotten 188 | %% about. Must return 1 msg_id per Ack, in the same order as Acks. 189 | -callback ack([ack()], state()) -> {msg_ids(), state()}. 190 | 191 | %% Reinsert messages into the queue which have already been delivered 192 | %% and were pending acknowledgement. 193 | -callback requeue([ack()], state()) -> {msg_ids(), state()}. 194 | 195 | %% Fold over messages by ack tag. The supplied function is called with 196 | %% each message, its ack tag, and an accumulator. 197 | -callback ackfold(msg_fun(A), A, state(), [ack()]) -> {A, state()}. 198 | 199 | %% Fold over all the messages in a queue and return the accumulated 200 | %% results, leaving the queue undisturbed. 201 | -callback fold(fun((rabbit_types:basic_message(), 202 | rabbit_types:message_properties(), 203 | boolean(), A) -> {('stop' | 'cont'), A}), 204 | A, state()) -> {A, state()}. 205 | 206 | %% How long is my queue? 207 | -callback len(state()) -> non_neg_integer(). 208 | 209 | %% Is my queue empty? 210 | -callback is_empty(state()) -> boolean(). 211 | 212 | %% What's the queue depth, where depth = length + number of pending acks 213 | -callback depth(state()) -> non_neg_integer(). 214 | 215 | %% For the next three functions, the assumption is that you're 216 | %% monitoring something like the ingress and egress rates of the 217 | %% queue. The RAM duration is thus the length of time represented by 218 | %% the messages held in RAM given the current rates. If you want to 219 | %% ignore all of this stuff, then do so, and return 0 in 220 | %% ram_duration/1. 221 | 222 | %% The target is to have no more messages in RAM than indicated by the 223 | %% duration and the current queue rates. 224 | -callback set_ram_duration_target(duration(), state()) -> state(). 225 | 226 | %% Optionally recalculate the duration internally (likely to be just 227 | %% update your internal rates), and report how many seconds the 228 | %% messages in RAM represent given the current rates of the queue. 229 | -callback ram_duration(state()) -> {duration(), state()}. 230 | 231 | %% Should 'timeout' be called as soon as the queue process can manage 232 | %% (either on an empty mailbox, or when a timer fires)? 233 | -callback needs_timeout(state()) -> 'false' | 'timed' | 'idle'. 234 | 235 | %% Called (eventually) after needs_timeout returns 'idle' or 'timed'. 236 | %% Note this may be called more than once for each 'idle' or 'timed' 237 | %% returned from needs_timeout 238 | -callback timeout(state()) -> state(). 239 | 240 | %% Called immediately before the queue hibernates. 241 | -callback handle_pre_hibernate(state()) -> state(). 242 | 243 | %% Called when more credit has become available for credit_flow. 244 | -callback resume(state()) -> state(). 245 | 246 | %% Used to help prioritisation in rabbit_amqqueue_process. The rate of 247 | %% inbound messages and outbound messages at the moment. 248 | -callback msg_rates(state()) -> {float(), float()}. 249 | 250 | -callback info(atom(), state()) -> any(). 251 | 252 | %% Passed a function to be invoked with the relevant backing queue's 253 | %% state. Useful for when the backing queue or other components need 254 | %% to pass functions into the backing queue. 255 | -callback invoke(atom(), fun ((atom(), A) -> A), state()) -> state(). 256 | 257 | %% Called prior to a publish or publish_delivered call. Allows the BQ 258 | %% to signal that it's already seen this message, (e.g. it was published 259 | %% or discarded previously) and thus the message should be dropped. 260 | -callback is_duplicate(rabbit_types:basic_message(), state()) 261 | -> {boolean(), state()}. 262 | 263 | -callback set_queue_mode(queue_mode(), state()) -> state(). 264 | 265 | -callback zip_msgs_and_acks(delivered_publish(), 266 | [ack()], Acc, state()) 267 | -> Acc. 268 | 269 | -else. 270 | 271 | -export([behaviour_info/1]). 272 | 273 | behaviour_info(callbacks) -> 274 | [{start, 1}, {stop, 0}, {init, 3}, {terminate, 2}, 275 | {delete_and_terminate, 2}, {delete_crashed, 1}, {purge, 1}, 276 | {purge_acks, 1}, {publish, 6}, {publish_delivered, 5}, 277 | {batch_publish, 4}, {batch_publish_delivered, 4}, 278 | {discard, 4}, {drain_confirmed, 1}, 279 | {dropwhile, 2}, {fetchwhile, 4}, {fetch, 2}, 280 | {drop, 2}, {ack, 2}, {requeue, 2}, {ackfold, 4}, {fold, 3}, {len, 1}, 281 | {is_empty, 1}, {depth, 1}, {set_ram_duration_target, 2}, 282 | {ram_duration, 1}, {needs_timeout, 1}, {timeout, 1}, 283 | {handle_pre_hibernate, 1}, {resume, 1}, {msg_rates, 1}, 284 | {info, 2}, {invoke, 3}, {is_duplicate, 2}, {set_queue_mode, 2}, 285 | {zip_msgs_and_acks, 4}]; 286 | behaviour_info(_Other) -> 287 | undefined. 288 | 289 | -endif. 290 | 291 | info_keys() -> ?INFO_KEYS. 292 | -------------------------------------------------------------------------------- /src/rabbit_basic.erl: -------------------------------------------------------------------------------- 1 | %% The contents of this file are subject to the Mozilla Public License 2 | %% Version 1.1 (the "License"); you may not use this file except in 3 | %% compliance with the License. You may obtain a copy of the License 4 | %% at http://www.mozilla.org/MPL/ 5 | %% 6 | %% Software distributed under the License is distributed on an "AS IS" 7 | %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 8 | %% the License for the specific language governing rights and 9 | %% limitations under the License. 10 | %% 11 | %% The Original Code is RabbitMQ. 12 | %% 13 | %% The Initial Developer of the Original Code is GoPivotal, Inc. 14 | %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved. 15 | %% 16 | 17 | -module(rabbit_basic). 18 | -include("rabbit.hrl"). 19 | -include("rabbit_framing.hrl"). 20 | 21 | -export([publish/4, publish/5, publish/1, 22 | message/3, message/4, properties/1, prepend_table_header/3, 23 | extract_headers/1, extract_timestamp/1, map_headers/2, delivery/4, 24 | header_routes/1, parse_expiration/1, header/2, header/3]). 25 | -export([build_content/2, from_content/1, msg_size/1, maybe_gc_large_msg/1]). 26 | 27 | %%---------------------------------------------------------------------------- 28 | 29 | -ifdef(use_specs). 30 | 31 | -type(properties_input() :: 32 | (rabbit_framing:amqp_property_record() | [{atom(), any()}])). 33 | -type(publish_result() :: 34 | ({ok, [pid()]} | rabbit_types:error('not_found'))). 35 | -type(header() :: any()). 36 | -type(headers() :: rabbit_framing:amqp_table() | 'undefined'). 37 | 38 | -type(exchange_input() :: (rabbit_types:exchange() | rabbit_exchange:name())). 39 | -type(body_input() :: (binary() | [binary()])). 40 | 41 | -spec(publish/4 :: 42 | (exchange_input(), rabbit_router:routing_key(), properties_input(), 43 | body_input()) -> publish_result()). 44 | -spec(publish/5 :: 45 | (exchange_input(), rabbit_router:routing_key(), boolean(), 46 | properties_input(), body_input()) -> publish_result()). 47 | -spec(publish/1 :: 48 | (rabbit_types:delivery()) -> publish_result()). 49 | -spec(delivery/4 :: 50 | (boolean(), boolean(), rabbit_types:message(), undefined | integer()) -> 51 | rabbit_types:delivery()). 52 | -spec(message/4 :: 53 | (rabbit_exchange:name(), rabbit_router:routing_key(), 54 | properties_input(), binary()) -> rabbit_types:message()). 55 | -spec(message/3 :: 56 | (rabbit_exchange:name(), rabbit_router:routing_key(), 57 | rabbit_types:decoded_content()) -> 58 | rabbit_types:ok_or_error2(rabbit_types:message(), any())). 59 | -spec(properties/1 :: 60 | (properties_input()) -> rabbit_framing:amqp_property_record()). 61 | 62 | -spec(prepend_table_header/3 :: 63 | (binary(), rabbit_framing:amqp_table(), headers()) -> headers()). 64 | 65 | -spec(header/2 :: 66 | (header(), headers()) -> 'undefined' | any()). 67 | -spec(header/3 :: 68 | (header(), headers(), any()) -> 'undefined' | any()). 69 | 70 | -spec(extract_headers/1 :: (rabbit_types:content()) -> headers()). 71 | 72 | -spec(map_headers/2 :: (fun((headers()) -> headers()), rabbit_types:content()) 73 | -> rabbit_types:content()). 74 | 75 | -spec(header_routes/1 :: 76 | (undefined | rabbit_framing:amqp_table()) -> [string()]). 77 | -spec(build_content/2 :: (rabbit_framing:amqp_property_record(), 78 | binary() | [binary()]) -> rabbit_types:content()). 79 | -spec(from_content/1 :: (rabbit_types:content()) -> 80 | {rabbit_framing:amqp_property_record(), binary()}). 81 | -spec(parse_expiration/1 :: 82 | (rabbit_framing:amqp_property_record()) 83 | -> rabbit_types:ok_or_error2('undefined' | non_neg_integer(), any())). 84 | 85 | -spec(msg_size/1 :: (rabbit_types:content() | rabbit_types:message()) -> 86 | non_neg_integer()). 87 | 88 | -spec(maybe_gc_large_msg/1 :: 89 | (rabbit_types:content() | rabbit_types:message()) -> non_neg_integer()). 90 | 91 | -endif. 92 | 93 | %%---------------------------------------------------------------------------- 94 | 95 | %% Convenience function, for avoiding round-trips in calls across the 96 | %% erlang distributed network. 97 | publish(Exchange, RoutingKeyBin, Properties, Body) -> 98 | publish(Exchange, RoutingKeyBin, false, Properties, Body). 99 | 100 | %% Convenience function, for avoiding round-trips in calls across the 101 | %% erlang distributed network. 102 | publish(X = #exchange{name = XName}, RKey, Mandatory, Props, Body) -> 103 | Message = message(XName, RKey, properties(Props), Body), 104 | publish(X, delivery(Mandatory, false, Message, undefined)); 105 | publish(XName, RKey, Mandatory, Props, Body) -> 106 | Message = message(XName, RKey, properties(Props), Body), 107 | publish(delivery(Mandatory, false, Message, undefined)). 108 | 109 | publish(Delivery = #delivery{ 110 | message = #basic_message{exchange_name = XName}}) -> 111 | case rabbit_exchange:lookup(XName) of 112 | {ok, X} -> publish(X, Delivery); 113 | Err -> Err 114 | end. 115 | 116 | publish(X, Delivery) -> 117 | Qs = rabbit_amqqueue:lookup(rabbit_exchange:route(X, Delivery)), 118 | DeliveredQPids = rabbit_amqqueue:deliver(Qs, Delivery), 119 | {ok, DeliveredQPids}. 120 | 121 | delivery(Mandatory, Confirm, Message, MsgSeqNo) -> 122 | #delivery{mandatory = Mandatory, confirm = Confirm, sender = self(), 123 | message = Message, msg_seq_no = MsgSeqNo, flow = noflow}. 124 | 125 | build_content(Properties, BodyBin) when is_binary(BodyBin) -> 126 | build_content(Properties, [BodyBin]); 127 | 128 | build_content(Properties, PFR) -> 129 | %% basic.publish hasn't changed so we can just hard-code amqp_0_9_1 130 | {ClassId, _MethodId} = 131 | rabbit_framing_amqp_0_9_1:method_id('basic.publish'), 132 | #content{class_id = ClassId, 133 | properties = Properties, 134 | properties_bin = none, 135 | protocol = none, 136 | payload_fragments_rev = PFR}. 137 | 138 | from_content(Content) -> 139 | #content{class_id = ClassId, 140 | properties = Props, 141 | payload_fragments_rev = FragmentsRev} = 142 | rabbit_binary_parser:ensure_content_decoded(Content), 143 | %% basic.publish hasn't changed so we can just hard-code amqp_0_9_1 144 | {ClassId, _MethodId} = 145 | rabbit_framing_amqp_0_9_1:method_id('basic.publish'), 146 | {Props, list_to_binary(lists:reverse(FragmentsRev))}. 147 | 148 | %% This breaks the spec rule forbidding message modification 149 | strip_header(#content{properties = #'P_basic'{headers = undefined}} 150 | = DecodedContent, _Key) -> 151 | DecodedContent; 152 | strip_header(#content{properties = Props = #'P_basic'{headers = Headers}} 153 | = DecodedContent, Key) -> 154 | case lists:keysearch(Key, 1, Headers) of 155 | false -> DecodedContent; 156 | {value, Found} -> Headers0 = lists:delete(Found, Headers), 157 | rabbit_binary_generator:clear_encoded_content( 158 | DecodedContent#content{ 159 | properties = Props#'P_basic'{ 160 | headers = Headers0}}) 161 | end. 162 | 163 | message(XName, RoutingKey, #content{properties = Props} = DecodedContent) -> 164 | try 165 | {ok, #basic_message{ 166 | exchange_name = XName, 167 | content = strip_header(DecodedContent, ?DELETED_HEADER), 168 | id = rabbit_guid:gen(), 169 | is_persistent = is_message_persistent(DecodedContent), 170 | routing_keys = [RoutingKey | 171 | header_routes(Props#'P_basic'.headers)]}} 172 | catch 173 | {error, _Reason} = Error -> Error 174 | end. 175 | 176 | message(XName, RoutingKey, RawProperties, Body) -> 177 | Properties = properties(RawProperties), 178 | Content = build_content(Properties, Body), 179 | {ok, Msg} = message(XName, RoutingKey, Content), 180 | Msg. 181 | 182 | properties(P = #'P_basic'{}) -> 183 | P; 184 | properties(P) when is_list(P) -> 185 | %% Yes, this is O(length(P) * record_info(size, 'P_basic') / 2), 186 | %% i.e. slow. Use the definition of 'P_basic' directly if 187 | %% possible! 188 | lists:foldl(fun ({Key, Value}, Acc) -> 189 | case indexof(record_info(fields, 'P_basic'), Key) of 190 | 0 -> throw({unknown_basic_property, Key}); 191 | N -> setelement(N + 1, Acc, Value) 192 | end 193 | end, #'P_basic'{}, P). 194 | 195 | prepend_table_header(Name, Info, undefined) -> 196 | prepend_table_header(Name, Info, []); 197 | prepend_table_header(Name, Info, Headers) -> 198 | case rabbit_misc:table_lookup(Headers, Name) of 199 | {array, Existing} -> 200 | prepend_table(Name, Info, Existing, Headers); 201 | undefined -> 202 | prepend_table(Name, Info, [], Headers); 203 | Other -> 204 | Headers2 = prepend_table(Name, Info, [], Headers), 205 | set_invalid_header(Name, Other, Headers2) 206 | end. 207 | 208 | prepend_table(Name, Info, Prior, Headers) -> 209 | rabbit_misc:set_table_value(Headers, Name, array, [{table, Info} | Prior]). 210 | 211 | set_invalid_header(Name, {_, _}=Value, Headers) when is_list(Headers) -> 212 | case rabbit_misc:table_lookup(Headers, ?INVALID_HEADERS_KEY) of 213 | undefined -> 214 | set_invalid([{Name, array, [Value]}], Headers); 215 | {table, ExistingHdr} -> 216 | update_invalid(Name, Value, ExistingHdr, Headers); 217 | Other -> 218 | %% somehow the x-invalid-headers header is corrupt 219 | Invalid = [{?INVALID_HEADERS_KEY, array, [Other]}], 220 | set_invalid_header(Name, Value, set_invalid(Invalid, Headers)) 221 | end. 222 | 223 | set_invalid(NewHdr, Headers) -> 224 | rabbit_misc:set_table_value(Headers, ?INVALID_HEADERS_KEY, table, NewHdr). 225 | 226 | update_invalid(Name, Value, ExistingHdr, Header) -> 227 | Values = case rabbit_misc:table_lookup(ExistingHdr, Name) of 228 | undefined -> [Value]; 229 | {array, Prior} -> [Value | Prior] 230 | end, 231 | NewHdr = rabbit_misc:set_table_value(ExistingHdr, Name, array, Values), 232 | set_invalid(NewHdr, Header). 233 | 234 | header(_Header, undefined) -> 235 | undefined; 236 | header(_Header, []) -> 237 | undefined; 238 | header(Header, Headers) -> 239 | header(Header, Headers, undefined). 240 | 241 | header(Header, Headers, Default) -> 242 | case lists:keysearch(Header, 1, Headers) of 243 | false -> Default; 244 | {value, Val} -> Val 245 | end. 246 | 247 | extract_headers(Content) -> 248 | #content{properties = #'P_basic'{headers = Headers}} = 249 | rabbit_binary_parser:ensure_content_decoded(Content), 250 | Headers. 251 | 252 | extract_timestamp(Content) -> 253 | #content{properties = #'P_basic'{timestamp = Timestamp}} = 254 | rabbit_binary_parser:ensure_content_decoded(Content), 255 | Timestamp. 256 | 257 | map_headers(F, Content) -> 258 | Content1 = rabbit_binary_parser:ensure_content_decoded(Content), 259 | #content{properties = #'P_basic'{headers = Headers} = Props} = Content1, 260 | Headers1 = F(Headers), 261 | rabbit_binary_generator:clear_encoded_content( 262 | Content1#content{properties = Props#'P_basic'{headers = Headers1}}). 263 | 264 | indexof(L, Element) -> indexof(L, Element, 1). 265 | 266 | indexof([], _Element, _N) -> 0; 267 | indexof([Element | _Rest], Element, N) -> N; 268 | indexof([_ | Rest], Element, N) -> indexof(Rest, Element, N + 1). 269 | 270 | is_message_persistent(#content{properties = #'P_basic'{ 271 | delivery_mode = Mode}}) -> 272 | case Mode of 273 | 1 -> false; 274 | 2 -> true; 275 | undefined -> false; 276 | Other -> throw({error, {delivery_mode_unknown, Other}}) 277 | end. 278 | 279 | %% Extract CC routes from headers 280 | header_routes(undefined) -> 281 | []; 282 | header_routes(HeadersTable) -> 283 | lists:append( 284 | [case rabbit_misc:table_lookup(HeadersTable, HeaderKey) of 285 | {array, Routes} -> [Route || {longstr, Route} <- Routes]; 286 | undefined -> []; 287 | {Type, _Val} -> throw({error, {unacceptable_type_in_header, 288 | binary_to_list(HeaderKey), Type}}) 289 | end || HeaderKey <- ?ROUTING_HEADERS]). 290 | 291 | parse_expiration(#'P_basic'{expiration = undefined}) -> 292 | {ok, undefined}; 293 | parse_expiration(#'P_basic'{expiration = Expiration}) -> 294 | case string:to_integer(binary_to_list(Expiration)) of 295 | {error, no_integer} = E -> 296 | E; 297 | {N, ""} -> 298 | case rabbit_misc:check_expiry(N) of 299 | ok -> {ok, N}; 300 | E = {error, _} -> E 301 | end; 302 | {_, S} -> 303 | {error, {leftover_string, S}} 304 | end. 305 | 306 | %% Some processes (channel, writer) can get huge amounts of binary 307 | %% garbage when processing huge messages at high speed (since we only 308 | %% do enough reductions to GC every few hundred messages, and if each 309 | %% message is 1MB then that's ugly). So count how many bytes of 310 | %% message we have processed, and force a GC every so often. 311 | maybe_gc_large_msg(Content) -> 312 | Size = msg_size(Content), 313 | Current = case get(msg_size_for_gc) of 314 | undefined -> 0; 315 | C -> C 316 | end, 317 | New = Current + Size, 318 | put(msg_size_for_gc, case New > 1000000 of 319 | true -> erlang:garbage_collect(), 320 | 0; 321 | false -> New 322 | end), 323 | Size. 324 | 325 | msg_size(#content{payload_fragments_rev = PFR}) -> iolist_size(PFR); 326 | msg_size(#basic_message{content = Content}) -> msg_size(Content). 327 | -------------------------------------------------------------------------------- /src/rabbit_binary_generator.erl: -------------------------------------------------------------------------------- 1 | %% The contents of this file are subject to the Mozilla Public License 2 | %% Version 1.1 (the "License"); you may not use this file except in 3 | %% compliance with the License. You may obtain a copy of the License 4 | %% at http://www.mozilla.org/MPL/ 5 | %% 6 | %% Software distributed under the License is distributed on an "AS IS" 7 | %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 8 | %% the License for the specific language governing rights and 9 | %% limitations under the License. 10 | %% 11 | %% The Original Code is RabbitMQ. 12 | %% 13 | %% The Initial Developer of the Original Code is GoPivotal, Inc. 14 | %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved. 15 | %% 16 | 17 | -module(rabbit_binary_generator). 18 | -include("rabbit_framing.hrl"). 19 | -include("rabbit.hrl"). 20 | 21 | -export([build_simple_method_frame/3, 22 | build_simple_content_frames/4, 23 | build_heartbeat_frame/0]). 24 | -export([generate_table/1]). 25 | -export([check_empty_frame_size/0]). 26 | -export([ensure_content_encoded/2, clear_encoded_content/1]). 27 | -export([map_exception/3]). 28 | 29 | %%---------------------------------------------------------------------------- 30 | 31 | -ifdef(use_specs). 32 | 33 | -type(frame() :: [binary()]). 34 | 35 | -spec(build_simple_method_frame/3 :: 36 | (rabbit_channel:channel_number(), rabbit_framing:amqp_method_record(), 37 | rabbit_types:protocol()) 38 | -> frame()). 39 | -spec(build_simple_content_frames/4 :: 40 | (rabbit_channel:channel_number(), rabbit_types:content(), 41 | non_neg_integer(), rabbit_types:protocol()) 42 | -> [frame()]). 43 | -spec(build_heartbeat_frame/0 :: () -> frame()). 44 | -spec(generate_table/1 :: (rabbit_framing:amqp_table()) -> binary()). 45 | -spec(check_empty_frame_size/0 :: () -> 'ok'). 46 | -spec(ensure_content_encoded/2 :: 47 | (rabbit_types:content(), rabbit_types:protocol()) -> 48 | rabbit_types:encoded_content()). 49 | -spec(clear_encoded_content/1 :: 50 | (rabbit_types:content()) -> rabbit_types:unencoded_content()). 51 | -spec(map_exception/3 :: (rabbit_channel:channel_number(), 52 | rabbit_types:amqp_error() | any(), 53 | rabbit_types:protocol()) -> 54 | {rabbit_channel:channel_number(), 55 | rabbit_framing:amqp_method_record()}). 56 | 57 | -endif. 58 | 59 | %%---------------------------------------------------------------------------- 60 | 61 | build_simple_method_frame(ChannelInt, MethodRecord, Protocol) -> 62 | MethodFields = Protocol:encode_method_fields(MethodRecord), 63 | MethodName = rabbit_misc:method_record_type(MethodRecord), 64 | {ClassId, MethodId} = Protocol:method_id(MethodName), 65 | create_frame(1, ChannelInt, [<>, MethodFields]). 66 | 67 | build_simple_content_frames(ChannelInt, Content, FrameMax, Protocol) -> 68 | #content{class_id = ClassId, 69 | properties_bin = ContentPropertiesBin, 70 | payload_fragments_rev = PayloadFragmentsRev} = 71 | ensure_content_encoded(Content, Protocol), 72 | {BodySize, ContentFrames} = 73 | build_content_frames(PayloadFragmentsRev, FrameMax, ChannelInt), 74 | HeaderFrame = create_frame(2, ChannelInt, 75 | [<>, 76 | ContentPropertiesBin]), 77 | [HeaderFrame | ContentFrames]. 78 | 79 | build_content_frames(FragsRev, FrameMax, ChannelInt) -> 80 | BodyPayloadMax = if FrameMax == 0 -> iolist_size(FragsRev); 81 | true -> FrameMax - ?EMPTY_FRAME_SIZE 82 | end, 83 | build_content_frames(0, [], BodyPayloadMax, [], 84 | lists:reverse(FragsRev), BodyPayloadMax, ChannelInt). 85 | 86 | build_content_frames(SizeAcc, FramesAcc, _FragSizeRem, [], 87 | [], _BodyPayloadMax, _ChannelInt) -> 88 | {SizeAcc, lists:reverse(FramesAcc)}; 89 | build_content_frames(SizeAcc, FramesAcc, FragSizeRem, FragAcc, 90 | Frags, BodyPayloadMax, ChannelInt) 91 | when FragSizeRem == 0 orelse Frags == [] -> 92 | Frame = create_frame(3, ChannelInt, lists:reverse(FragAcc)), 93 | FrameSize = BodyPayloadMax - FragSizeRem, 94 | build_content_frames(SizeAcc + FrameSize, [Frame | FramesAcc], 95 | BodyPayloadMax, [], Frags, BodyPayloadMax, ChannelInt); 96 | build_content_frames(SizeAcc, FramesAcc, FragSizeRem, FragAcc, 97 | [Frag | Frags], BodyPayloadMax, ChannelInt) -> 98 | Size = size(Frag), 99 | {NewFragSizeRem, NewFragAcc, NewFrags} = 100 | if Size == 0 -> {FragSizeRem, FragAcc, Frags}; 101 | Size =< FragSizeRem -> {FragSizeRem - Size, [Frag | FragAcc], Frags}; 102 | true -> <> = 103 | Frag, 104 | {0, [Head | FragAcc], [Tail | Frags]} 105 | end, 106 | build_content_frames(SizeAcc, FramesAcc, NewFragSizeRem, NewFragAcc, 107 | NewFrags, BodyPayloadMax, ChannelInt). 108 | 109 | build_heartbeat_frame() -> 110 | create_frame(?FRAME_HEARTBEAT, 0, <<>>). 111 | 112 | create_frame(TypeInt, ChannelInt, Payload) -> 113 | [<>, Payload, 114 | ?FRAME_END]. 115 | 116 | %% table_field_to_binary supports the AMQP 0-8/0-9 standard types, S, 117 | %% I, D, T and F, as well as the QPid extensions b, d, f, l, s, t, x, 118 | %% and V. 119 | table_field_to_binary({FName, T, V}) -> 120 | [short_string_to_binary(FName) | field_value_to_binary(T, V)]. 121 | 122 | field_value_to_binary(longstr, V) -> [$S | long_string_to_binary(V)]; 123 | field_value_to_binary(signedint, V) -> [$I, <>]; 124 | field_value_to_binary(decimal, V) -> {Before, After} = V, 125 | [$D, Before, <>]; 126 | field_value_to_binary(timestamp, V) -> [$T, <>]; 127 | field_value_to_binary(table, V) -> [$F | table_to_binary(V)]; 128 | field_value_to_binary(array, V) -> [$A | array_to_binary(V)]; 129 | field_value_to_binary(byte, V) -> [$b, <>]; 130 | field_value_to_binary(double, V) -> [$d, <>]; 131 | field_value_to_binary(float, V) -> [$f, <>]; 132 | field_value_to_binary(long, V) -> [$l, <>]; 133 | field_value_to_binary(short, V) -> [$s, <>]; 134 | field_value_to_binary(bool, V) -> [$t, if V -> 1; true -> 0 end]; 135 | field_value_to_binary(binary, V) -> [$x | long_string_to_binary(V)]; 136 | field_value_to_binary(unsignedbyte, V) -> [$B, <>]; 137 | field_value_to_binary(unsignedshort, V) -> [$u, <>]; 138 | field_value_to_binary(unsignedint, V) -> [$i, <>]; 139 | field_value_to_binary(void, _V) -> [$V]. 140 | 141 | table_to_binary(Table) when is_list(Table) -> 142 | BinTable = generate_table_iolist(Table), 143 | [<<(iolist_size(BinTable)):32>> | BinTable]. 144 | 145 | array_to_binary(Array) when is_list(Array) -> 146 | BinArray = generate_array_iolist(Array), 147 | [<<(iolist_size(BinArray)):32>> | BinArray]. 148 | 149 | generate_table(Table) when is_list(Table) -> 150 | list_to_binary(generate_table_iolist(Table)). 151 | 152 | generate_table_iolist(Table) -> 153 | lists:map(fun table_field_to_binary/1, Table). 154 | 155 | generate_array_iolist(Array) -> 156 | lists:map(fun ({T, V}) -> field_value_to_binary(T, V) end, Array). 157 | 158 | short_string_to_binary(String) -> 159 | Len = string_length(String), 160 | if Len < 256 -> [<>, String]; 161 | true -> exit(content_properties_shortstr_overflow) 162 | end. 163 | 164 | long_string_to_binary(String) -> 165 | Len = string_length(String), 166 | [<>, String]. 167 | 168 | string_length(String) when is_binary(String) -> size(String); 169 | string_length(String) -> length(String). 170 | 171 | check_empty_frame_size() -> 172 | %% Intended to ensure that EMPTY_FRAME_SIZE is defined correctly. 173 | case iolist_size(create_frame(?FRAME_BODY, 0, <<>>)) of 174 | ?EMPTY_FRAME_SIZE -> ok; 175 | ComputedSize -> exit({incorrect_empty_frame_size, 176 | ComputedSize, ?EMPTY_FRAME_SIZE}) 177 | end. 178 | 179 | ensure_content_encoded(Content = #content{properties_bin = PropBin, 180 | protocol = Protocol}, Protocol) 181 | when PropBin =/= none -> 182 | Content; 183 | ensure_content_encoded(Content = #content{properties = none, 184 | properties_bin = PropBin, 185 | protocol = Protocol}, Protocol1) 186 | when PropBin =/= none -> 187 | Props = Protocol:decode_properties(Content#content.class_id, PropBin), 188 | Content#content{properties = Props, 189 | properties_bin = Protocol1:encode_properties(Props), 190 | protocol = Protocol1}; 191 | ensure_content_encoded(Content = #content{properties = Props}, Protocol) 192 | when Props =/= none -> 193 | Content#content{properties_bin = Protocol:encode_properties(Props), 194 | protocol = Protocol}. 195 | 196 | clear_encoded_content(Content = #content{properties_bin = none, 197 | protocol = none}) -> 198 | Content; 199 | clear_encoded_content(Content = #content{properties = none}) -> 200 | %% Only clear when we can rebuild the properties_bin later in 201 | %% accordance to the content record definition comment - maximum 202 | %% one of properties and properties_bin can be 'none' 203 | Content; 204 | clear_encoded_content(Content = #content{}) -> 205 | Content#content{properties_bin = none, protocol = none}. 206 | 207 | %% NB: this function is also used by the Erlang client 208 | map_exception(Channel, Reason, Protocol) -> 209 | {SuggestedClose, ReplyCode, ReplyText, FailedMethod} = 210 | lookup_amqp_exception(Reason, Protocol), 211 | {ClassId, MethodId} = case FailedMethod of 212 | {_, _} -> FailedMethod; 213 | none -> {0, 0}; 214 | _ -> Protocol:method_id(FailedMethod) 215 | end, 216 | case SuggestedClose orelse (Channel == 0) of 217 | true -> {0, #'connection.close'{reply_code = ReplyCode, 218 | reply_text = ReplyText, 219 | class_id = ClassId, 220 | method_id = MethodId}}; 221 | false -> {Channel, #'channel.close'{reply_code = ReplyCode, 222 | reply_text = ReplyText, 223 | class_id = ClassId, 224 | method_id = MethodId}} 225 | end. 226 | 227 | lookup_amqp_exception(#amqp_error{name = Name, 228 | explanation = Expl, 229 | method = Method}, 230 | Protocol) -> 231 | {ShouldClose, Code, Text} = Protocol:lookup_amqp_exception(Name), 232 | ExplBin = amqp_exception_explanation(Text, Expl), 233 | {ShouldClose, Code, ExplBin, Method}; 234 | lookup_amqp_exception(Other, Protocol) -> 235 | rabbit_log:warning("Non-AMQP exit reason '~p'~n", [Other]), 236 | {ShouldClose, Code, Text} = Protocol:lookup_amqp_exception(internal_error), 237 | {ShouldClose, Code, Text, none}. 238 | 239 | amqp_exception_explanation(Text, Expl) -> 240 | ExplBin = list_to_binary(Expl), 241 | CompleteTextBin = <>, 242 | if size(CompleteTextBin) > 255 -> <>; 243 | true -> CompleteTextBin 244 | end. 245 | -------------------------------------------------------------------------------- /src/rabbit_binary_parser.erl: -------------------------------------------------------------------------------- 1 | %% The contents of this file are subject to the Mozilla Public License 2 | %% Version 1.1 (the "License"); you may not use this file except in 3 | %% compliance with the License. You may obtain a copy of the License 4 | %% at http://www.mozilla.org/MPL/ 5 | %% 6 | %% Software distributed under the License is distributed on an "AS IS" 7 | %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 8 | %% the License for the specific language governing rights and 9 | %% limitations under the License. 10 | %% 11 | %% The Original Code is RabbitMQ. 12 | %% 13 | %% The Initial Developer of the Original Code is GoPivotal, Inc. 14 | %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved. 15 | %% 16 | 17 | -module(rabbit_binary_parser). 18 | 19 | -include("rabbit.hrl"). 20 | 21 | -export([parse_table/1]). 22 | -export([ensure_content_decoded/1, clear_decoded_content/1]). 23 | -export([validate_utf8/1, assert_utf8/1]). 24 | 25 | %%---------------------------------------------------------------------------- 26 | 27 | -ifdef(use_specs). 28 | 29 | -spec(parse_table/1 :: (binary()) -> rabbit_framing:amqp_table()). 30 | -spec(ensure_content_decoded/1 :: 31 | (rabbit_types:content()) -> rabbit_types:decoded_content()). 32 | -spec(clear_decoded_content/1 :: 33 | (rabbit_types:content()) -> rabbit_types:undecoded_content()). 34 | -spec(validate_utf8/1 :: (binary()) -> 'ok' | 'error'). 35 | -spec(assert_utf8/1 :: (binary()) -> 'ok'). 36 | 37 | -endif. 38 | 39 | %%---------------------------------------------------------------------------- 40 | 41 | %% parse_table supports the AMQP 0-8/0-9 standard types, S, I, D, T 42 | %% and F, as well as the QPid extensions b, d, f, l, s, t, x, and V. 43 | 44 | -define(SIMPLE_PARSE_TABLE(BType, Pattern, RType), 45 | parse_table(<>) -> 47 | [{NameString, RType, Value} | parse_table(Rest)]). 48 | 49 | %% Note that we try to put these in approximately the order we expect 50 | %% to hit them, that's why the empty binary is half way through. 51 | 52 | parse_table(<>) -> 54 | [{NameString, longstr, Value} | parse_table(Rest)]; 55 | 56 | ?SIMPLE_PARSE_TABLE($T, Value:64/unsigned, timestamp); 57 | 58 | parse_table(<<>>) -> 59 | []; 60 | 61 | ?SIMPLE_PARSE_TABLE($b, Value:8/signed, byte); 62 | ?SIMPLE_PARSE_TABLE($B, Value:8/unsigned, unsignedbyte); 63 | 64 | ?SIMPLE_PARSE_TABLE($s, Value:16/signed, short); 65 | ?SIMPLE_PARSE_TABLE($u, Value:16/unsigned, unsignedshort); 66 | 67 | ?SIMPLE_PARSE_TABLE($I, Value:32/signed, signedint); 68 | ?SIMPLE_PARSE_TABLE($i, Value:32/unsigned, unsignedint); 69 | 70 | ?SIMPLE_PARSE_TABLE($d, Value:64/float, double); 71 | ?SIMPLE_PARSE_TABLE($f, Value:32/float, float); 72 | 73 | ?SIMPLE_PARSE_TABLE($l, Value:64/signed, long); 74 | 75 | 76 | parse_table(<>) -> 78 | [{NameString, bool, (Value /= 0)} | parse_table(Rest)]; 79 | 80 | parse_table(<>) -> 82 | [{NameString, decimal, {Before, After}} | parse_table(Rest)]; 83 | 84 | parse_table(<>) -> 86 | [{NameString, table, parse_table(Value)} | parse_table(Rest)]; 87 | 88 | parse_table(<>) -> 90 | [{NameString, array, parse_array(Value)} | parse_table(Rest)]; 91 | 92 | parse_table(<>) -> 94 | [{NameString, binary, Value} | parse_table(Rest)]; 95 | 96 | parse_table(<>) -> 98 | [{NameString, void, undefined} | parse_table(Rest)]. 99 | 100 | -define(SIMPLE_PARSE_ARRAY(BType, Pattern, RType), 101 | parse_array(<>) -> 102 | [{RType, Value} | parse_array(Rest)]). 103 | 104 | parse_array(<<$S, VLen:32/unsigned, Value:VLen/binary, Rest/binary>>) -> 105 | [{longstr, Value} | parse_array(Rest)]; 106 | 107 | ?SIMPLE_PARSE_ARRAY($T, Value:64/unsigned, timestamp); 108 | 109 | parse_array(<<>>) -> 110 | []; 111 | 112 | ?SIMPLE_PARSE_ARRAY($b, Value:8/signed, byte); 113 | ?SIMPLE_PARSE_ARRAY($B, Value:8/unsigned, unsignedbyte); 114 | 115 | ?SIMPLE_PARSE_ARRAY($s, Value:16/signed, short); 116 | ?SIMPLE_PARSE_ARRAY($u, Value:16/unsigned, unsignedshort); 117 | 118 | ?SIMPLE_PARSE_ARRAY($I, Value:32/signed, signedint); 119 | ?SIMPLE_PARSE_ARRAY($i, Value:32/unsigned, unsignedint); 120 | 121 | ?SIMPLE_PARSE_ARRAY($d, Value:64/float, double); 122 | ?SIMPLE_PARSE_ARRAY($f, Value:32/float, float); 123 | 124 | ?SIMPLE_PARSE_ARRAY($l, Value:64/signed, long); 125 | 126 | 127 | 128 | parse_array(<<$t, Value:8/unsigned, Rest/binary>>) -> 129 | [{bool, (Value /= 0)} | parse_array(Rest)]; 130 | 131 | parse_array(<<$D, Before:8/unsigned, After:32/unsigned, Rest/binary>>) -> 132 | [{decimal, {Before, After}} | parse_array(Rest)]; 133 | 134 | parse_array(<<$F, VLen:32/unsigned, Value:VLen/binary, Rest/binary>>) -> 135 | [{table, parse_table(Value)} | parse_array(Rest)]; 136 | 137 | parse_array(<<$A, VLen:32/unsigned, Value:VLen/binary, Rest/binary>>) -> 138 | [{array, parse_array(Value)} | parse_array(Rest)]; 139 | 140 | parse_array(<<$x, VLen:32/unsigned, Value:VLen/binary, Rest/binary>>) -> 141 | [{binary, Value} | parse_array(Rest)]; 142 | 143 | parse_array(<<$V, Rest/binary>>) -> 144 | [{void, undefined} | parse_array(Rest)]. 145 | 146 | ensure_content_decoded(Content = #content{properties = Props}) 147 | when Props =/= none -> 148 | Content; 149 | ensure_content_decoded(Content = #content{properties_bin = PropBin, 150 | protocol = Protocol}) 151 | when PropBin =/= none -> 152 | Content#content{properties = Protocol:decode_properties( 153 | Content#content.class_id, PropBin)}. 154 | 155 | clear_decoded_content(Content = #content{properties = none}) -> 156 | Content; 157 | clear_decoded_content(Content = #content{properties_bin = none}) -> 158 | %% Only clear when we can rebuild the properties later in 159 | %% accordance to the content record definition comment - maximum 160 | %% one of properties and properties_bin can be 'none' 161 | Content; 162 | clear_decoded_content(Content = #content{}) -> 163 | Content#content{properties = none}. 164 | 165 | assert_utf8(B) -> 166 | case validate_utf8(B) of 167 | ok -> ok; 168 | error -> rabbit_misc:protocol_error( 169 | frame_error, "Malformed UTF-8 in shortstr", []) 170 | end. 171 | 172 | validate_utf8(Bin) -> 173 | try 174 | xmerl_ucs:from_utf8(Bin), 175 | ok 176 | catch exit:{ucs, _} -> 177 | error 178 | end. 179 | -------------------------------------------------------------------------------- /src/rabbit_channel_interceptor.erl: -------------------------------------------------------------------------------- 1 | %% The contents of this file are subject to the Mozilla Public License 2 | %% Version 1.1 (the "License"); you may not use this file except in 3 | %% compliance with the License. You may obtain a copy of the License 4 | %% at http://www.mozilla.org/MPL/ 5 | %% 6 | %% Software distributed under the License is distributed on an "AS IS" 7 | %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 8 | %% the License for the specific language governing rights and 9 | %% limitations under the License. 10 | %% 11 | %% The Original Code is RabbitMQ. 12 | %% 13 | %% The Initial Developer of the Original Code is GoPivotal, Inc. 14 | %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved. 15 | %% 16 | 17 | -module(rabbit_channel_interceptor). 18 | 19 | -include("rabbit_framing.hrl"). 20 | -include("rabbit.hrl"). 21 | 22 | -export([init/1, intercept_in/3]). 23 | 24 | -ifdef(use_specs). 25 | 26 | -type(method_name() :: rabbit_framing:amqp_method_name()). 27 | -type(original_method() :: rabbit_framing:amqp_method_record()). 28 | -type(processed_method() :: rabbit_framing:amqp_method_record()). 29 | -type(original_content() :: rabbit_types:maybe(rabbit_types:content())). 30 | -type(processed_content() :: rabbit_types:maybe(rabbit_types:content())). 31 | -type(interceptor_state() :: term()). 32 | 33 | -callback description() -> [proplists:property()]. 34 | %% Derive some initial state from the channel. This will be passed back 35 | %% as the third argument of intercept/3. 36 | -callback init(rabbit_channel:channel()) -> interceptor_state(). 37 | -callback intercept(original_method(), original_content(), 38 | interceptor_state()) -> 39 | {processed_method(), processed_content()} | 40 | rabbit_misc:channel_or_connection_exit(). 41 | -callback applies_to() -> list(method_name()). 42 | 43 | -else. 44 | 45 | -export([behaviour_info/1]). 46 | 47 | behaviour_info(callbacks) -> 48 | [{description, 0}, {init, 1}, {intercept, 3}, {applies_to, 0}]; 49 | behaviour_info(_Other) -> 50 | undefined. 51 | 52 | -endif. 53 | 54 | init(Ch) -> 55 | Mods = [M || {_, M} <- rabbit_registry:lookup_all(channel_interceptor)], 56 | check_no_overlap(Mods), 57 | [{Mod, Mod:init(Ch)} || Mod <- Mods]. 58 | 59 | check_no_overlap(Mods) -> 60 | check_no_overlap1([sets:from_list(Mod:applies_to()) || Mod <- Mods]). 61 | 62 | %% Check no non-empty pairwise intersection in a list of sets 63 | check_no_overlap1(Sets) -> 64 | lists:foldl(fun(Set, Union) -> 65 | Is = sets:intersection(Set, Union), 66 | case sets:size(Is) of 67 | 0 -> ok; 68 | _ -> 69 | internal_error("Interceptor: more than one " 70 | "module handles ~p~n", [Is]) 71 | end, 72 | sets:union(Set, Union) 73 | end, 74 | sets:new(), 75 | Sets), 76 | ok. 77 | 78 | intercept_in(M, C, Mods) -> 79 | lists:foldl(fun({Mod, ModState}, {M1, C1}) -> 80 | call_module(Mod, ModState, M1, C1) 81 | end, 82 | {M, C}, 83 | Mods). 84 | 85 | call_module(Mod, St, M, C) -> 86 | % this little dance is because Mod might be unloaded at any point 87 | case (catch {ok, Mod:intercept(M, C, St)}) of 88 | {ok, R} -> validate_response(Mod, M, C, R); 89 | {'EXIT', {undef, [{Mod, intercept, _, _} | _]}} -> {M, C} 90 | end. 91 | 92 | validate_response(Mod, M1, C1, R = {M2, C2}) -> 93 | case {validate_method(M1, M2), validate_content(C1, C2)} of 94 | {true, true} -> R; 95 | {false, _} -> 96 | internal_error("Interceptor: ~p expected to return " 97 | "method: ~p but returned: ~p", 98 | [Mod, rabbit_misc:method_record_type(M1), 99 | rabbit_misc:method_record_type(M2)]); 100 | {_, false} -> 101 | internal_error("Interceptor: ~p expected to return " 102 | "content iff content is provided but " 103 | "content in = ~p; content out = ~p", 104 | [Mod, C1, C2]) 105 | end. 106 | 107 | validate_method(M, M2) -> 108 | rabbit_misc:method_record_type(M) =:= rabbit_misc:method_record_type(M2). 109 | 110 | validate_content(none, none) -> true; 111 | validate_content(#content{}, #content{}) -> true; 112 | validate_content(_, _) -> false. 113 | 114 | %% keep dialyzer happy 115 | -spec internal_error(string(), [any()]) -> no_return(). 116 | internal_error(Format, Args) -> 117 | rabbit_misc:protocol_error(internal_error, Format, Args). 118 | -------------------------------------------------------------------------------- /src/rabbit_command_assembler.erl: -------------------------------------------------------------------------------- 1 | %% The contents of this file are subject to the Mozilla Public License 2 | %% Version 1.1 (the "License"); you may not use this file except in 3 | %% compliance with the License. You may obtain a copy of the License 4 | %% at http://www.mozilla.org/MPL/ 5 | %% 6 | %% Software distributed under the License is distributed on an "AS IS" 7 | %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 8 | %% the License for the specific language governing rights and 9 | %% limitations under the License. 10 | %% 11 | %% The Original Code is RabbitMQ. 12 | %% 13 | %% The Initial Developer of the Original Code is GoPivotal, Inc. 14 | %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved. 15 | %% 16 | 17 | -module(rabbit_command_assembler). 18 | -include("rabbit_framing.hrl"). 19 | -include("rabbit.hrl"). 20 | 21 | -export([analyze_frame/3, init/1, process/2]). 22 | 23 | %%---------------------------------------------------------------------------- 24 | 25 | %%---------------------------------------------------------------------------- 26 | 27 | -ifdef(use_specs). 28 | 29 | -export_type([frame/0]). 30 | 31 | -type(frame_type() :: ?FRAME_METHOD | ?FRAME_HEADER | ?FRAME_BODY | 32 | ?FRAME_OOB_METHOD | ?FRAME_OOB_HEADER | ?FRAME_OOB_BODY | 33 | ?FRAME_TRACE | ?FRAME_HEARTBEAT). 34 | -type(protocol() :: rabbit_framing:protocol()). 35 | -type(method() :: rabbit_framing:amqp_method_record()). 36 | -type(class_id() :: rabbit_framing:amqp_class_id()). 37 | -type(weight() :: non_neg_integer()). 38 | -type(body_size() :: non_neg_integer()). 39 | -type(content() :: rabbit_types:undecoded_content()). 40 | 41 | -type(frame() :: 42 | {'method', rabbit_framing:amqp_method_name(), binary()} | 43 | {'content_header', class_id(), weight(), body_size(), binary()} | 44 | {'content_body', binary()}). 45 | 46 | -type(state() :: 47 | {'method', protocol()} | 48 | {'content_header', method(), class_id(), protocol()} | 49 | {'content_body', method(), body_size(), class_id(), protocol()}). 50 | 51 | -spec(analyze_frame/3 :: (frame_type(), binary(), protocol()) -> 52 | frame() | 'heartbeat' | 'error'). 53 | 54 | -spec(init/1 :: (protocol()) -> {ok, state()}). 55 | -spec(process/2 :: (frame(), state()) -> 56 | {ok, state()} | 57 | {ok, method(), state()} | 58 | {ok, method(), content(), state()} | 59 | {error, rabbit_types:amqp_error()}). 60 | 61 | -endif. 62 | 63 | %%-------------------------------------------------------------------- 64 | 65 | analyze_frame(?FRAME_METHOD, 66 | <>, 67 | Protocol) -> 68 | MethodName = Protocol:lookup_method_name({ClassId, MethodId}), 69 | {method, MethodName, MethodFields}; 70 | analyze_frame(?FRAME_HEADER, 71 | <>, 72 | _Protocol) -> 73 | {content_header, ClassId, Weight, BodySize, Properties}; 74 | analyze_frame(?FRAME_BODY, Body, _Protocol) -> 75 | {content_body, Body}; 76 | analyze_frame(?FRAME_HEARTBEAT, <<>>, _Protocol) -> 77 | heartbeat; 78 | analyze_frame(_Type, _Body, _Protocol) -> 79 | error. 80 | 81 | init(Protocol) -> {ok, {method, Protocol}}. 82 | 83 | process({method, MethodName, FieldsBin}, {method, Protocol}) -> 84 | try 85 | Method = Protocol:decode_method_fields(MethodName, FieldsBin), 86 | case Protocol:method_has_content(MethodName) of 87 | true -> {ClassId, _MethodId} = Protocol:method_id(MethodName), 88 | {ok, {content_header, Method, ClassId, Protocol}}; 89 | false -> {ok, Method, {method, Protocol}} 90 | end 91 | catch exit:#amqp_error{} = Reason -> {error, Reason} 92 | end; 93 | process(_Frame, {method, _Protocol}) -> 94 | unexpected_frame("expected method frame, " 95 | "got non method frame instead", [], none); 96 | process({content_header, ClassId, 0, 0, PropertiesBin}, 97 | {content_header, Method, ClassId, Protocol}) -> 98 | Content = empty_content(ClassId, PropertiesBin, Protocol), 99 | {ok, Method, Content, {method, Protocol}}; 100 | process({content_header, ClassId, 0, BodySize, PropertiesBin}, 101 | {content_header, Method, ClassId, Protocol}) -> 102 | Content = empty_content(ClassId, PropertiesBin, Protocol), 103 | {ok, {content_body, Method, BodySize, Content, Protocol}}; 104 | process({content_header, HeaderClassId, 0, _BodySize, _PropertiesBin}, 105 | {content_header, Method, ClassId, _Protocol}) -> 106 | unexpected_frame("expected content header for class ~w, " 107 | "got one for class ~w instead", 108 | [ClassId, HeaderClassId], Method); 109 | process(_Frame, {content_header, Method, ClassId, _Protocol}) -> 110 | unexpected_frame("expected content header for class ~w, " 111 | "got non content header frame instead", [ClassId], Method); 112 | process({content_body, FragmentBin}, 113 | {content_body, Method, RemainingSize, 114 | Content = #content{payload_fragments_rev = Fragments}, Protocol}) -> 115 | NewContent = Content#content{ 116 | payload_fragments_rev = [FragmentBin | Fragments]}, 117 | case RemainingSize - size(FragmentBin) of 118 | 0 -> {ok, Method, NewContent, {method, Protocol}}; 119 | Sz -> {ok, {content_body, Method, Sz, NewContent, Protocol}} 120 | end; 121 | process(_Frame, {content_body, Method, _RemainingSize, _Content, _Protocol}) -> 122 | unexpected_frame("expected content body, " 123 | "got non content body frame instead", [], Method). 124 | 125 | %%-------------------------------------------------------------------- 126 | 127 | empty_content(ClassId, PropertiesBin, Protocol) -> 128 | #content{class_id = ClassId, 129 | properties = none, 130 | properties_bin = PropertiesBin, 131 | protocol = Protocol, 132 | payload_fragments_rev = []}. 133 | 134 | unexpected_frame(Format, Params, Method) when is_atom(Method) -> 135 | {error, rabbit_misc:amqp_error(unexpected_frame, Format, Params, Method)}; 136 | unexpected_frame(Format, Params, Method) -> 137 | unexpected_frame(Format, Params, rabbit_misc:method_record_type(Method)). 138 | -------------------------------------------------------------------------------- /src/rabbit_common.app.src: -------------------------------------------------------------------------------- 1 | % vim:ft=erlang: 2 | 3 | {application, rabbit_common, [ 4 | {description, ""}, 5 | {vsn, ""}, 6 | {id, "git"}, 7 | {modules, []}, 8 | {registered, []}, 9 | {applications, [ 10 | kernel, 11 | stdlib 12 | ]} 13 | ]}. 14 | -------------------------------------------------------------------------------- /src/rabbit_control_misc.erl: -------------------------------------------------------------------------------- 1 | %% The contents of this file are subject to the Mozilla Public License 2 | %% Version 1.1 (the "License"); you may not use this file except in 3 | %% compliance with the License. You may obtain a copy of the License 4 | %% at http://www.mozilla.org/MPL/ 5 | %% 6 | %% Software distributed under the License is distributed on an "AS IS" 7 | %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 8 | %% the License for the specific language governing rights and 9 | %% limitations under the License. 10 | %% 11 | %% The Original Code is RabbitMQ. 12 | %% 13 | %% The Initial Developer of the Original Code is GoPivotal, Inc. 14 | %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved. 15 | %% 16 | 17 | -module(rabbit_control_misc). 18 | 19 | -export([emitting_map/4, emitting_map/5, emitting_map_with_exit_handler/4, 20 | emitting_map_with_exit_handler/5, wait_for_info_messages/5, 21 | print_cmd_result/2]). 22 | 23 | -ifdef(use_specs). 24 | 25 | -spec(emitting_map/4 :: (pid(), reference(), fun(), list()) -> 'ok'). 26 | -spec(emitting_map/5 :: (pid(), reference(), fun(), list(), atom()) -> 'ok'). 27 | -spec(emitting_map_with_exit_handler/4 :: 28 | (pid(), reference(), fun(), list()) -> 'ok'). 29 | -spec(emitting_map_with_exit_handler/5 :: 30 | (pid(), reference(), fun(), list(), atom()) -> 'ok'). 31 | -spec(print_cmd_result/2 :: (atom(), term()) -> 'ok'). 32 | 33 | -endif. 34 | 35 | emitting_map(AggregatorPid, Ref, Fun, List) -> 36 | emitting_map(AggregatorPid, Ref, Fun, List, continue), 37 | AggregatorPid ! {Ref, finished}, 38 | ok. 39 | 40 | emitting_map(AggregatorPid, Ref, Fun, List, continue) -> 41 | _ = emitting_map0(AggregatorPid, Ref, Fun, List, fun step/4), 42 | ok. 43 | 44 | emitting_map_with_exit_handler(AggregatorPid, Ref, Fun, List) -> 45 | emitting_map_with_exit_handler(AggregatorPid, Ref, Fun, List, continue), 46 | AggregatorPid ! {Ref, finished}, 47 | ok. 48 | 49 | emitting_map_with_exit_handler(AggregatorPid, Ref, Fun, List, continue) -> 50 | _ = emitting_map0(AggregatorPid, Ref, Fun, List, fun step_with_exit_handler/4), 51 | ok. 52 | 53 | emitting_map0(AggregatorPid, Ref, Fun, List, StepFun) -> 54 | [StepFun(AggregatorPid, Ref, Fun, Item) || Item <- List]. 55 | 56 | step(AggregatorPid, Ref, Fun, Item) -> 57 | AggregatorPid ! {Ref, Fun(Item), continue}, 58 | ok. 59 | 60 | step_with_exit_handler(AggregatorPid, Ref, Fun, Item) -> 61 | Noop = make_ref(), 62 | case rabbit_misc:with_exit_handler( 63 | fun () -> Noop end, 64 | fun () -> Fun(Item) end) of 65 | Noop -> 66 | ok; 67 | Res -> 68 | AggregatorPid ! {Ref, Res, continue}, 69 | ok 70 | end. 71 | 72 | wait_for_info_messages(Pid, Ref, ArgAtoms, DisplayFun, Timeout) -> 73 | _ = notify_if_timeout(Pid, Ref, Timeout), 74 | wait_for_info_messages(Ref, ArgAtoms, DisplayFun). 75 | 76 | wait_for_info_messages(Ref, InfoItemKeys, DisplayFun) when is_reference(Ref) -> 77 | receive 78 | {Ref, finished} -> 79 | ok; 80 | {Ref, {timeout, T}} -> 81 | exit({error, {timeout, (T / 1000)}}); 82 | {Ref, []} -> 83 | wait_for_info_messages(Ref, InfoItemKeys, DisplayFun); 84 | {Ref, Result, continue} -> 85 | DisplayFun(Result, InfoItemKeys), 86 | wait_for_info_messages(Ref, InfoItemKeys, DisplayFun); 87 | {error, Error} -> 88 | Error; 89 | _ -> 90 | wait_for_info_messages(Ref, InfoItemKeys, DisplayFun) 91 | end. 92 | 93 | notify_if_timeout(Pid, Ref, Timeout) -> 94 | timer:send_after(Timeout, Pid, {Ref, {timeout, Timeout}}). 95 | 96 | print_cmd_result(authenticate_user, _Result) -> io:format("Success~n"). 97 | -------------------------------------------------------------------------------- /src/rabbit_data_coercion.erl: -------------------------------------------------------------------------------- 1 | %% The contents of this file are subject to the Mozilla Public License 2 | %% Version 1.1 (the "License"); you may not use this file except in 3 | %% compliance with the License. You may obtain a copy of the License 4 | %% at http://www.mozilla.org/MPL/ 5 | %% 6 | %% Software distributed under the License is distributed on an "AS IS" 7 | %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 8 | %% the License for the specific language governing rights and 9 | %% limitations under the License. 10 | %% 11 | %% The Original Code is RabbitMQ. 12 | %% 13 | %% The Initial Developer of the Original Code is GoPivotal, Inc. 14 | %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved. 15 | %% 16 | 17 | -module(rabbit_data_coercion). 18 | 19 | -export([to_binary/1]). 20 | 21 | to_binary(Val) when is_list(Val) -> list_to_binary(Val); 22 | to_binary(Val) -> Val. 23 | -------------------------------------------------------------------------------- /src/rabbit_error_logger_handler.erl: -------------------------------------------------------------------------------- 1 | %% The contents of this file are subject to the Mozilla Public License 2 | %% Version 1.1 (the "License"); you may not use this file except in 3 | %% compliance with the License. You may obtain a copy of the License 4 | %% at http://www.mozilla.org/MPL/ 5 | %% 6 | %% Software distributed under the License is distributed on an "AS IS" 7 | %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 8 | %% the License for the specific language governing rights and 9 | %% limitations under the License. 10 | %% 11 | %% The Original Code is RabbitMQ. 12 | %% 13 | %% The Initial Developer of the Original Code is GoPivotal, Inc. 14 | %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved. 15 | %% 16 | -module(rabbit_error_logger_handler). 17 | 18 | -behaviour(gen_event). 19 | 20 | %% API 21 | -export([start_link/0, add_handler/0]). 22 | 23 | %% gen_event callbacks 24 | -export([init/1, handle_event/2, handle_call/2, 25 | handle_info/2, terminate/2, code_change/3]). 26 | 27 | -define(SERVER, ?MODULE). 28 | 29 | -record(state, {report = []}). 30 | 31 | %%%=================================================================== 32 | %%% API 33 | %%%=================================================================== 34 | 35 | %%-------------------------------------------------------------------- 36 | %% @doc 37 | %% Creates an event manager 38 | %% 39 | %% @spec start_link() -> {ok, Pid} | {error, Error} 40 | %% @end 41 | %%-------------------------------------------------------------------- 42 | start_link() -> 43 | gen_event:start_link({local, ?SERVER}). 44 | 45 | %%-------------------------------------------------------------------- 46 | %% @doc 47 | %% Adds an event handler 48 | %% 49 | %% @spec add_handler() -> ok | {'EXIT', Reason} | term() 50 | %% @end 51 | %%-------------------------------------------------------------------- 52 | add_handler() -> 53 | gen_event:add_handler(?SERVER, ?MODULE, []). 54 | 55 | %%%=================================================================== 56 | %%% gen_event callbacks 57 | %%%=================================================================== 58 | 59 | %%-------------------------------------------------------------------- 60 | %% @private 61 | %% @doc 62 | %% Whenever a new event handler is added to an event manager, 63 | %% this function is called to initialize the event handler. 64 | %% 65 | %% @spec init(Args) -> {ok, State} 66 | %% @end 67 | %%-------------------------------------------------------------------- 68 | init([]) -> 69 | {ok, #state{}}. 70 | 71 | %%-------------------------------------------------------------------- 72 | %% @private 73 | %% @doc 74 | %% Whenever an event manager receives an event sent using 75 | %% gen_event:notify/2 or gen_event:sync_notify/2, this function is 76 | %% called for each installed event handler to handle the event. 77 | %% 78 | %% @spec handle_event(Event, State) -> 79 | %% {ok, State} | 80 | %% {swap_handler, Args1, State1, Mod2, Args2} | 81 | %% remove_handler 82 | %% @end 83 | %%-------------------------------------------------------------------- 84 | 85 | handle_event({info_report, _Gleader, {_Pid, _Type, 86 | {net_kernel, {'EXIT', _, Reason}}}}, 87 | #state{report = Report} = State) -> 88 | NewReport = case format(Reason) of 89 | [] -> Report; 90 | Formatted -> [Formatted | Report] 91 | end, 92 | {ok, State#state{report = NewReport}}; 93 | handle_event(_Event, State) -> 94 | {ok, State}. 95 | 96 | %%-------------------------------------------------------------------- 97 | %% @private 98 | %% @doc 99 | %% Whenever an event manager receives a request sent using 100 | %% gen_event:call/3,4, this function is called for the specified 101 | %% event handler to handle the request. 102 | %% 103 | %% @spec handle_call(Request, State) -> 104 | %% {ok, Reply, State} | 105 | %% {swap_handler, Reply, Args1, State1, Mod2, Args2} | 106 | %% {remove_handler, Reply} 107 | %% @end 108 | %%-------------------------------------------------------------------- 109 | handle_call(get_connection_report, State) -> 110 | {ok, lists:reverse(State#state.report), State#state{report = []}}; 111 | handle_call(_Request, State) -> 112 | Reply = ok, 113 | {ok, Reply, State}. 114 | 115 | %%-------------------------------------------------------------------- 116 | %% @private 117 | %% @doc 118 | %% This function is called for each installed event handler when 119 | %% an event manager receives any other message than an event or a 120 | %% synchronous request (or a system message). 121 | %% 122 | %% @spec handle_info(Info, State) -> 123 | %% {ok, State} | 124 | %% {swap_handler, Args1, State1, Mod2, Args2} | 125 | %% remove_handler 126 | %% @end 127 | %%-------------------------------------------------------------------- 128 | handle_info(_Info, State) -> 129 | {ok, State}. 130 | 131 | %%-------------------------------------------------------------------- 132 | %% @private 133 | %% @doc 134 | %% Whenever an event handler is deleted from an event manager, this 135 | %% function is called. It should be the opposite of Module:init/1 and 136 | %% do any necessary cleaning up. 137 | %% 138 | %% @spec terminate(Reason, State) -> void() 139 | %% @end 140 | %%-------------------------------------------------------------------- 141 | terminate(_Reason, _State) -> 142 | ok. 143 | 144 | %%-------------------------------------------------------------------- 145 | %% @private 146 | %% @doc 147 | %% Convert process state when code is changed 148 | %% 149 | %% @spec code_change(OldVsn, State, Extra) -> {ok, NewState} 150 | %% @end 151 | %%-------------------------------------------------------------------- 152 | code_change(_OldVsn, State, _Extra) -> 153 | {ok, State}. 154 | 155 | %%%=================================================================== 156 | %%% Internal functions 157 | %%%=================================================================== 158 | format({check_dflag_xnc_failed, _What}) -> 159 | {" * Remote node uses an incompatible Erlang version ~n", []}; 160 | format({recv_challenge_failed, no_node, Node}) -> 161 | {" * Hostname mismatch: node ~p believes its host is different. Please ensure that hostnames resolve the same way locally and on ~p~n", [Node, Node]}; 162 | format({recv_challenge_failed, Error}) -> 163 | {" * Distribution failed unexpectedly while waiting for challenge: ~p~n", [Error]}; 164 | format({recv_challenge_ack_failed, bad_cookie}) -> 165 | {" * Authentication failed (rejected by the local node), please check the Erlang cookie~n", []}; 166 | format({recv_challenge_ack_failed, {error, closed}}) -> 167 | {" * Authentication failed (rejected by the remote node), please check the Erlang cookie~n", []}; 168 | format({recv_status_failed, not_allowed}) -> 169 | {" * This node is not on the list of nodes authorised by remote node (see net_kernel:allow/1)~n", []}; 170 | format({recv_status_failed, {error, closed}}) -> 171 | {" * Remote host closed TCP connection before completing authentication. Is the Erlang distribution using TLS?~n", []}; 172 | format(setup_timer_timeout) -> 173 | {" * TCP connection to remote host has timed out. Is the Erlang distribution using TLS?~n", []}; 174 | format(_) -> 175 | []. 176 | -------------------------------------------------------------------------------- /src/rabbit_event.erl: -------------------------------------------------------------------------------- 1 | %% The contents of this file are subject to the Mozilla Public License 2 | %% Version 1.1 (the "License"); you may not use this file except in 3 | %% compliance with the License. You may obtain a copy of the License 4 | %% at http://www.mozilla.org/MPL/ 5 | %% 6 | %% Software distributed under the License is distributed on an "AS IS" 7 | %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 8 | %% the License for the specific language governing rights and 9 | %% limitations under the License. 10 | %% 11 | %% The Original Code is RabbitMQ. 12 | %% 13 | %% The Initial Developer of the Original Code is GoPivotal, Inc. 14 | %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved. 15 | %% 16 | 17 | -module(rabbit_event). 18 | 19 | -include("rabbit.hrl"). 20 | 21 | -export([start_link/0]). 22 | -export([init_stats_timer/2, init_disabled_stats_timer/2, 23 | ensure_stats_timer/3, stop_stats_timer/2, reset_stats_timer/2]). 24 | -export([stats_level/2, if_enabled/3]). 25 | -export([notify/2, notify/3, notify_if/3]). 26 | -export([sync_notify/2, sync_notify/3]). 27 | 28 | %%---------------------------------------------------------------------------- 29 | 30 | -record(state, {level, interval, timer}). 31 | 32 | %%---------------------------------------------------------------------------- 33 | 34 | -ifdef(use_specs). 35 | 36 | -export_type([event_type/0, event_props/0, event_timestamp/0, event/0]). 37 | 38 | -type(event_type() :: atom()). 39 | -type(event_props() :: term()). 40 | -type(event_timestamp() :: non_neg_integer()). 41 | 42 | -type(event() :: #event { type :: event_type(), 43 | props :: event_props(), 44 | reference :: 'none' | reference(), 45 | timestamp :: event_timestamp() }). 46 | 47 | -type(level() :: 'none' | 'coarse' | 'fine'). 48 | 49 | -type(timer_fun() :: fun (() -> 'ok')). 50 | -type(container() :: tuple()). 51 | -type(pos() :: non_neg_integer()). 52 | 53 | -spec(start_link/0 :: () -> rabbit_types:ok_pid_or_error()). 54 | -spec(init_stats_timer/2 :: (container(), pos()) -> container()). 55 | -spec(init_disabled_stats_timer/2 :: (container(), pos()) -> container()). 56 | -spec(ensure_stats_timer/3 :: (container(), pos(), term()) -> container()). 57 | -spec(stop_stats_timer/2 :: (container(), pos()) -> container()). 58 | -spec(reset_stats_timer/2 :: (container(), pos()) -> container()). 59 | -spec(stats_level/2 :: (container(), pos()) -> level()). 60 | -spec(if_enabled/3 :: (container(), pos(), timer_fun()) -> 'ok'). 61 | -spec(notify/2 :: (event_type(), event_props()) -> 'ok'). 62 | -spec(notify/3 :: (event_type(), event_props(), reference() | 'none') -> 'ok'). 63 | -spec(notify_if/3 :: (boolean(), event_type(), event_props()) -> 'ok'). 64 | -spec(sync_notify/2 :: (event_type(), event_props()) -> 'ok'). 65 | -spec(sync_notify/3 :: (event_type(), event_props(), 66 | reference() | 'none') -> 'ok'). 67 | 68 | -endif. 69 | 70 | %%---------------------------------------------------------------------------- 71 | 72 | start_link() -> 73 | gen_event:start_link({local, ?MODULE}). 74 | 75 | %% The idea is, for each stat-emitting object: 76 | %% 77 | %% On startup: 78 | %% init_stats_timer(State) 79 | %% notify(created event) 80 | %% if_enabled(internal_emit_stats) - so we immediately send something 81 | %% 82 | %% On wakeup: 83 | %% ensure_stats_timer(State, emit_stats) 84 | %% (Note we can't emit stats immediately, the timer may have fired 1ms ago.) 85 | %% 86 | %% emit_stats: 87 | %% if_enabled(internal_emit_stats) 88 | %% reset_stats_timer(State) - just bookkeeping 89 | %% 90 | %% Pre-hibernation: 91 | %% if_enabled(internal_emit_stats) 92 | %% stop_stats_timer(State) 93 | %% 94 | %% internal_emit_stats: 95 | %% notify(stats) 96 | 97 | init_stats_timer(C, P) -> 98 | {ok, StatsLevel} = application:get_env(rabbit, collect_statistics), 99 | {ok, Interval} = application:get_env(rabbit, collect_statistics_interval), 100 | setelement(P, C, #state{level = StatsLevel, interval = Interval, 101 | timer = undefined}). 102 | 103 | init_disabled_stats_timer(C, P) -> 104 | setelement(P, C, #state{level = none, interval = 0, timer = undefined}). 105 | 106 | ensure_stats_timer(C, P, Msg) -> 107 | case element(P, C) of 108 | #state{level = Level, interval = Interval, timer = undefined} = State 109 | when Level =/= none -> 110 | TRef = erlang:send_after(Interval, self(), Msg), 111 | setelement(P, C, State#state{timer = TRef}); 112 | #state{} -> 113 | C 114 | end. 115 | 116 | stop_stats_timer(C, P) -> 117 | case element(P, C) of 118 | #state{timer = TRef} = State when TRef =/= undefined -> 119 | case erlang:cancel_timer(TRef) of 120 | false -> C; 121 | _ -> setelement(P, C, State#state{timer = undefined}) 122 | end; 123 | #state{} -> 124 | C 125 | end. 126 | 127 | reset_stats_timer(C, P) -> 128 | case element(P, C) of 129 | #state{timer = TRef} = State when TRef =/= undefined -> 130 | setelement(P, C, State#state{timer = undefined}); 131 | #state{} -> 132 | C 133 | end. 134 | 135 | stats_level(C, P) -> 136 | #state{level = Level} = element(P, C), 137 | Level. 138 | 139 | if_enabled(C, P, Fun) -> 140 | case element(P, C) of 141 | #state{level = none} -> ok; 142 | #state{} -> Fun(), ok 143 | end. 144 | 145 | notify_if(true, Type, Props) -> notify(Type, Props); 146 | notify_if(false, _Type, _Props) -> ok. 147 | 148 | notify(Type, Props) -> notify(Type, Props, none). 149 | 150 | notify(Type, Props, Ref) -> 151 | gen_event:notify(?MODULE, event_cons(Type, Props, Ref)). 152 | 153 | sync_notify(Type, Props) -> sync_notify(Type, Props, none). 154 | 155 | sync_notify(Type, Props, Ref) -> 156 | gen_event:sync_notify(?MODULE, event_cons(Type, Props, Ref)). 157 | 158 | event_cons(Type, Props, Ref) -> 159 | #event{type = Type, 160 | props = Props, 161 | reference = Ref, 162 | timestamp = time_compat:os_system_time(milli_seconds)}. 163 | 164 | -------------------------------------------------------------------------------- /src/rabbit_exchange_decorator.erl: -------------------------------------------------------------------------------- 1 | %% The contents of this file are subject to the Mozilla Public License 2 | %% Version 1.1 (the "License"); you may not use this file except in 3 | %% compliance with the License. You may obtain a copy of the License 4 | %% at http://www.mozilla.org/MPL/ 5 | %% 6 | %% Software distributed under the License is distributed on an "AS IS" 7 | %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 8 | %% the License for the specific language governing rights and 9 | %% limitations under the License. 10 | %% 11 | %% The Original Code is RabbitMQ. 12 | %% 13 | %% The Initial Developer of the Original Code is GoPivotal, Inc. 14 | %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved. 15 | %% 16 | 17 | -module(rabbit_exchange_decorator). 18 | 19 | -include("rabbit.hrl"). 20 | 21 | -export([select/2, set/1, register/2, unregister/1]). 22 | 23 | %% This is like an exchange type except that: 24 | %% 25 | %% 1) It applies to all exchanges as soon as it is installed, therefore 26 | %% 2) It is not allowed to affect validation, so no validate/1 or 27 | %% assert_args_equivalence/2 28 | %% 29 | %% It's possible in the future we might make decorators 30 | %% able to manipulate messages as they are published. 31 | 32 | -ifdef(use_specs). 33 | 34 | -type(tx() :: 'transaction' | 'none'). 35 | -type(serial() :: pos_integer() | tx()). 36 | 37 | -callback description() -> [proplists:property()]. 38 | 39 | %% Should Rabbit ensure that all binding events that are 40 | %% delivered to an individual exchange can be serialised? (they 41 | %% might still be delivered out of order, but there'll be a 42 | %% serial number). 43 | -callback serialise_events(rabbit_types:exchange()) -> boolean(). 44 | 45 | %% called after declaration and recovery 46 | -callback create(tx(), rabbit_types:exchange()) -> 'ok'. 47 | 48 | %% called after exchange (auto)deletion. 49 | -callback delete(tx(), rabbit_types:exchange(), [rabbit_types:binding()]) -> 50 | 'ok'. 51 | 52 | %% called when the policy attached to this exchange changes. 53 | -callback policy_changed(rabbit_types:exchange(), rabbit_types:exchange()) -> 54 | 'ok'. 55 | 56 | %% called after a binding has been added or recovered 57 | -callback add_binding(serial(), rabbit_types:exchange(), 58 | rabbit_types:binding()) -> 'ok'. 59 | 60 | %% called after bindings have been deleted. 61 | -callback remove_bindings(serial(), rabbit_types:exchange(), 62 | [rabbit_types:binding()]) -> 'ok'. 63 | 64 | %% Allows additional destinations to be added to the routing decision. 65 | -callback route(rabbit_types:exchange(), rabbit_types:delivery()) -> 66 | [rabbit_amqqueue:name() | rabbit_exchange:name()]. 67 | 68 | %% Whether the decorator wishes to receive callbacks for the exchange 69 | %% none:no callbacks, noroute:all callbacks except route, all:all callbacks 70 | -callback active_for(rabbit_types:exchange()) -> 'none' | 'noroute' | 'all'. 71 | 72 | -else. 73 | 74 | -export([behaviour_info/1]). 75 | 76 | behaviour_info(callbacks) -> 77 | [{description, 0}, {serialise_events, 1}, {create, 2}, {delete, 3}, 78 | {policy_changed, 2}, {add_binding, 3}, {remove_bindings, 3}, 79 | {route, 2}, {active_for, 1}]; 80 | behaviour_info(_Other) -> 81 | undefined. 82 | 83 | -endif. 84 | 85 | %%---------------------------------------------------------------------------- 86 | 87 | %% select a subset of active decorators 88 | select(all, {Route, NoRoute}) -> filter(Route ++ NoRoute); 89 | select(route, {Route, _NoRoute}) -> filter(Route); 90 | select(raw, {Route, NoRoute}) -> Route ++ NoRoute. 91 | 92 | filter(Modules) -> 93 | [M || M <- Modules, code:which(M) =/= non_existing]. 94 | 95 | set(X) -> 96 | Decs = lists:foldl(fun (D, {Route, NoRoute}) -> 97 | ActiveFor = D:active_for(X), 98 | {cons_if_eq(all, ActiveFor, D, Route), 99 | cons_if_eq(noroute, ActiveFor, D, NoRoute)} 100 | end, {[], []}, list()), 101 | X#exchange{decorators = Decs}. 102 | 103 | list() -> [M || {_, M} <- rabbit_registry:lookup_all(exchange_decorator)]. 104 | 105 | cons_if_eq(Select, Select, Item, List) -> [Item | List]; 106 | cons_if_eq(_Select, _Other, _Item, List) -> List. 107 | 108 | register(TypeName, ModuleName) -> 109 | rabbit_registry:register(exchange_decorator, TypeName, ModuleName), 110 | [maybe_recover(X) || X <- rabbit_exchange:list()], 111 | ok. 112 | 113 | unregister(TypeName) -> 114 | rabbit_registry:unregister(exchange_decorator, TypeName), 115 | [maybe_recover(X) || X <- rabbit_exchange:list()], 116 | ok. 117 | 118 | maybe_recover(X = #exchange{name = Name, 119 | decorators = Decs}) -> 120 | #exchange{decorators = Decs1} = set(X), 121 | Old = lists:sort(select(all, Decs)), 122 | New = lists:sort(select(all, Decs1)), 123 | case New of 124 | Old -> ok; 125 | _ -> %% TODO create a tx here for non-federation decorators 126 | [M:create(none, X) || M <- New -- Old], 127 | rabbit_exchange:update_decorators(Name) 128 | end. 129 | -------------------------------------------------------------------------------- /src/rabbit_exchange_type.erl: -------------------------------------------------------------------------------- 1 | %% The contents of this file are subject to the Mozilla Public License 2 | %% Version 1.1 (the "License"); you may not use this file except in 3 | %% compliance with the License. You may obtain a copy of the License 4 | %% at http://www.mozilla.org/MPL/ 5 | %% 6 | %% Software distributed under the License is distributed on an "AS IS" 7 | %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 8 | %% the License for the specific language governing rights and 9 | %% limitations under the License. 10 | %% 11 | %% The Original Code is RabbitMQ. 12 | %% 13 | %% The Initial Developer of the Original Code is GoPivotal, Inc. 14 | %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved. 15 | %% 16 | 17 | -module(rabbit_exchange_type). 18 | 19 | -ifdef(use_specs). 20 | 21 | -type(tx() :: 'transaction' | 'none'). 22 | -type(serial() :: pos_integer() | tx()). 23 | 24 | -callback description() -> [proplists:property()]. 25 | 26 | %% Should Rabbit ensure that all binding events that are 27 | %% delivered to an individual exchange can be serialised? (they 28 | %% might still be delivered out of order, but there'll be a 29 | %% serial number). 30 | -callback serialise_events() -> boolean(). 31 | 32 | %% The no_return is there so that we can have an "invalid" exchange 33 | %% type (see rabbit_exchange_type_invalid). 34 | -callback route(rabbit_types:exchange(), rabbit_types:delivery()) -> 35 | rabbit_router:match_result(). 36 | 37 | %% called BEFORE declaration, to check args etc; may exit with #amqp_error{} 38 | -callback validate(rabbit_types:exchange()) -> 'ok'. 39 | 40 | %% called BEFORE declaration, to check args etc 41 | -callback validate_binding(rabbit_types:exchange(), rabbit_types:binding()) -> 42 | rabbit_types:ok_or_error({'binding_invalid', string(), [any()]}). 43 | 44 | %% called after declaration and recovery 45 | -callback create(tx(), rabbit_types:exchange()) -> 'ok'. 46 | 47 | %% called after exchange (auto)deletion. 48 | -callback delete(tx(), rabbit_types:exchange(), [rabbit_types:binding()]) -> 49 | 'ok'. 50 | 51 | %% called when the policy attached to this exchange changes. 52 | -callback policy_changed(rabbit_types:exchange(), rabbit_types:exchange()) -> 53 | 'ok'. 54 | 55 | %% called after a binding has been added or recovered 56 | -callback add_binding(serial(), rabbit_types:exchange(), 57 | rabbit_types:binding()) -> 'ok'. 58 | 59 | %% called after bindings have been deleted. 60 | -callback remove_bindings(serial(), rabbit_types:exchange(), 61 | [rabbit_types:binding()]) -> 'ok'. 62 | 63 | %% called when comparing exchanges for equivalence - should return ok or 64 | %% exit with #amqp_error{} 65 | -callback assert_args_equivalence(rabbit_types:exchange(), 66 | rabbit_framing:amqp_table()) -> 67 | 'ok' | rabbit_types:connection_exit(). 68 | 69 | -else. 70 | 71 | -export([behaviour_info/1]). 72 | 73 | behaviour_info(callbacks) -> 74 | [{description, 0}, {serialise_events, 0}, {route, 2}, 75 | {validate, 1}, {validate_binding, 2}, {policy_changed, 2}, 76 | {create, 2}, {delete, 3}, {add_binding, 3}, {remove_bindings, 3}, 77 | {assert_args_equivalence, 2}]; 78 | behaviour_info(_Other) -> 79 | undefined. 80 | 81 | -endif. 82 | -------------------------------------------------------------------------------- /src/rabbit_health_check.erl: -------------------------------------------------------------------------------- 1 | %% The contents of this file are subject to the Mozilla Public License 2 | %% Version 1.1 (the "License"); you may not use this file except in 3 | %% compliance with the License. You may obtain a copy of the License 4 | %% at http://www.mozilla.org/MPL/ 5 | %% 6 | %% Software distributed under the License is distributed on an "AS IS" 7 | %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 8 | %% the License for the specific language governing rights and 9 | %% limitations under the License. 10 | %% 11 | %% The Original Code is RabbitMQ. 12 | %% 13 | %% The Initial Developer of the Original Code is GoPivotal, Inc. 14 | %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved. 15 | %% 16 | -module(rabbit_health_check). 17 | 18 | -export([node/1]). 19 | 20 | -define(NODE_HEALTH_CHECK_TIMEOUT, 70000). 21 | 22 | -ifdef(use_specs). 23 | -spec(node/1 :: (node()) -> 'true' | no_return()). 24 | -endif. 25 | 26 | %%---------------------------------------------------------------------------- 27 | %% External functions 28 | %%---------------------------------------------------------------------------- 29 | node(Node) -> 30 | node_health_check(Node, is_running), 31 | node_health_check(Node, list_channels), 32 | node_health_check(Node, list_queues), 33 | node_health_check(Node, alarms). 34 | 35 | %%---------------------------------------------------------------------------- 36 | %% Internal functions 37 | %%---------------------------------------------------------------------------- 38 | node_health_check(Node, is_running) -> 39 | node_health_check( 40 | Node, {rabbit, is_running, []}, 41 | fun(true) -> 42 | true; 43 | (false) -> 44 | throw({node_is_ko, "rabbit application is not running", 70}) 45 | end); 46 | node_health_check(Node, list_channels) -> 47 | node_health_check( 48 | Node, {rabbit_channel, info_all, [[pid]]}, 49 | fun(L) when is_list(L) -> 50 | true; 51 | (Other) -> 52 | ErrorMsg = io_lib:format("list_channels unexpected output: ~p", 53 | [Other]), 54 | throw({node_is_ko, ErrorMsg, 70}) 55 | end); 56 | node_health_check(Node, list_queues) -> 57 | node_health_check( 58 | Node, {rabbit_amqqueue, info_all, [[pid]]}, 59 | fun(L) when is_list(L) -> 60 | true; 61 | (Other) -> 62 | ErrorMsg = io_lib:format("list_queues unexpected output: ~p", 63 | [Other]), 64 | throw({node_is_ko, ErrorMsg, 70}) 65 | end); 66 | node_health_check(Node, alarms) -> 67 | node_health_check( 68 | Node, {rabbit, status, []}, 69 | fun(Props) -> 70 | case proplists:get_value(alarms, Props) of 71 | [] -> 72 | true; 73 | Alarms -> 74 | ErrorMsg = io_lib:format("resource alarm(s) in effect:~p", [Alarms]), 75 | throw({node_is_ko, ErrorMsg, 70}) 76 | end 77 | end). 78 | 79 | node_health_check(Node, {M, F, A}, Fun) -> 80 | case rabbit_misc:rpc_call(Node, M, F, A, ?NODE_HEALTH_CHECK_TIMEOUT) of 81 | {badrpc, timeout} -> 82 | ErrorMsg = io_lib:format( 83 | "health check of node ~p fails: timed out (~p ms)", 84 | [Node, ?NODE_HEALTH_CHECK_TIMEOUT]), 85 | throw({node_is_ko, ErrorMsg, 70}); 86 | {badrpc, nodedown} -> 87 | ErrorMsg = io_lib:format( 88 | "health check of node ~p fails: nodedown", [Node]), 89 | throw({node_is_ko, ErrorMsg, 68}); 90 | {badrpc, Reason} -> 91 | ErrorMsg = io_lib:format( 92 | "health check of node ~p fails: ~p", [Node, Reason]), 93 | throw({node_is_ko, ErrorMsg, 70}); 94 | Other -> 95 | Fun(Other) 96 | end. 97 | 98 | 99 | -------------------------------------------------------------------------------- /src/rabbit_heartbeat.erl: -------------------------------------------------------------------------------- 1 | %% The contents of this file are subject to the Mozilla Public License 2 | %% Version 1.1 (the "License"); you may not use this file except in 3 | %% compliance with the License. You may obtain a copy of the License 4 | %% at http://www.mozilla.org/MPL/ 5 | %% 6 | %% Software distributed under the License is distributed on an "AS IS" 7 | %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 8 | %% the License for the specific language governing rights and 9 | %% limitations under the License. 10 | %% 11 | %% The Original Code is RabbitMQ. 12 | %% 13 | %% The Initial Developer of the Original Code is GoPivotal, Inc. 14 | %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved. 15 | %% 16 | 17 | -module(rabbit_heartbeat). 18 | 19 | -export([start/6, start/7]). 20 | -export([start_heartbeat_sender/4, start_heartbeat_receiver/4, 21 | pause_monitor/1, resume_monitor/1]). 22 | 23 | -export([system_continue/3, system_terminate/4, system_code_change/4]). 24 | 25 | -include("rabbit.hrl"). 26 | 27 | %%---------------------------------------------------------------------------- 28 | 29 | -ifdef(use_specs). 30 | 31 | -export_type([heartbeaters/0]). 32 | 33 | -type(heartbeaters() :: {rabbit_types:maybe(pid()), rabbit_types:maybe(pid())}). 34 | 35 | -type(heartbeat_callback() :: fun (() -> any())). 36 | 37 | -spec(start/6 :: 38 | (pid(), rabbit_net:socket(), 39 | non_neg_integer(), heartbeat_callback(), 40 | non_neg_integer(), heartbeat_callback()) -> heartbeaters()). 41 | 42 | -spec(start/7 :: 43 | (pid(), rabbit_net:socket(), rabbit_types:proc_name(), 44 | non_neg_integer(), heartbeat_callback(), 45 | non_neg_integer(), heartbeat_callback()) -> heartbeaters()). 46 | 47 | -spec(start_heartbeat_sender/4 :: 48 | (rabbit_net:socket(), non_neg_integer(), heartbeat_callback(), 49 | rabbit_types:proc_type_and_name()) -> rabbit_types:ok(pid())). 50 | -spec(start_heartbeat_receiver/4 :: 51 | (rabbit_net:socket(), non_neg_integer(), heartbeat_callback(), 52 | rabbit_types:proc_type_and_name()) -> rabbit_types:ok(pid())). 53 | 54 | -spec(pause_monitor/1 :: (heartbeaters()) -> 'ok'). 55 | -spec(resume_monitor/1 :: (heartbeaters()) -> 'ok'). 56 | 57 | -spec(system_code_change/4 :: (_,_,_,_) -> {'ok',_}). 58 | -spec(system_continue/3 :: (_,_,{_, _}) -> any()). 59 | -spec(system_terminate/4 :: (_,_,_,_) -> none()). 60 | 61 | -endif. 62 | 63 | %%---------------------------------------------------------------------------- 64 | start(SupPid, Sock, SendTimeoutSec, SendFun, ReceiveTimeoutSec, ReceiveFun) -> 65 | start(SupPid, Sock, unknown, 66 | SendTimeoutSec, SendFun, ReceiveTimeoutSec, ReceiveFun). 67 | 68 | start(SupPid, Sock, Identity, 69 | SendTimeoutSec, SendFun, ReceiveTimeoutSec, ReceiveFun) -> 70 | {ok, Sender} = 71 | start_heartbeater(SendTimeoutSec, SupPid, Sock, 72 | SendFun, heartbeat_sender, 73 | start_heartbeat_sender, Identity), 74 | {ok, Receiver} = 75 | start_heartbeater(ReceiveTimeoutSec, SupPid, Sock, 76 | ReceiveFun, heartbeat_receiver, 77 | start_heartbeat_receiver, Identity), 78 | {Sender, Receiver}. 79 | 80 | start_heartbeat_sender(Sock, TimeoutSec, SendFun, Identity) -> 81 | %% the 'div 2' is there so that we don't end up waiting for nearly 82 | %% 2 * TimeoutSec before sending a heartbeat in the boundary case 83 | %% where the last message was sent just after a heartbeat. 84 | heartbeater({Sock, TimeoutSec * 1000 div 2, send_oct, 0, 85 | fun () -> SendFun(), continue end}, Identity). 86 | 87 | start_heartbeat_receiver(Sock, TimeoutSec, ReceiveFun, Identity) -> 88 | %% we check for incoming data every interval, and time out after 89 | %% two checks with no change. As a result we will time out between 90 | %% 2 and 3 intervals after the last data has been received. 91 | heartbeater({Sock, TimeoutSec * 1000, recv_oct, 1, 92 | fun () -> ReceiveFun(), stop end}, Identity). 93 | 94 | pause_monitor({_Sender, none}) -> ok; 95 | pause_monitor({_Sender, Receiver}) -> Receiver ! pause, ok. 96 | 97 | resume_monitor({_Sender, none}) -> ok; 98 | resume_monitor({_Sender, Receiver}) -> Receiver ! resume, ok. 99 | 100 | system_continue(_Parent, Deb, {Params, State}) -> 101 | heartbeater(Params, Deb, State). 102 | 103 | system_terminate(Reason, _Parent, _Deb, _State) -> 104 | exit(Reason). 105 | 106 | system_code_change(Misc, _Module, _OldVsn, _Extra) -> 107 | {ok, Misc}. 108 | 109 | %%---------------------------------------------------------------------------- 110 | start_heartbeater(0, _SupPid, _Sock, _TimeoutFun, _Name, _Callback, 111 | _Identity) -> 112 | {ok, none}; 113 | start_heartbeater(TimeoutSec, SupPid, Sock, TimeoutFun, Name, Callback, 114 | Identity) -> 115 | supervisor2:start_child( 116 | SupPid, {Name, 117 | {rabbit_heartbeat, Callback, 118 | [Sock, TimeoutSec, TimeoutFun, {Name, Identity}]}, 119 | transient, ?MAX_WAIT, worker, [rabbit_heartbeat]}). 120 | 121 | heartbeater(Params, Identity) -> 122 | Deb = sys:debug_options([]), 123 | {ok, proc_lib:spawn_link(fun () -> 124 | rabbit_misc:store_proc_name(Identity), 125 | heartbeater(Params, Deb, {0, 0}) 126 | end)}. 127 | 128 | heartbeater({Sock, TimeoutMillisec, StatName, Threshold, Handler} = Params, 129 | Deb, {StatVal, SameCount} = State) -> 130 | Recurse = fun (State1) -> heartbeater(Params, Deb, State1) end, 131 | System = fun (From, Req) -> 132 | sys:handle_system_msg( 133 | Req, From, self(), ?MODULE, Deb, {Params, State}) 134 | end, 135 | receive 136 | pause -> 137 | receive 138 | resume -> Recurse({0, 0}); 139 | {system, From, Req} -> System(From, Req); 140 | Other -> exit({unexpected_message, Other}) 141 | end; 142 | {system, From, Req} -> 143 | System(From, Req); 144 | Other -> 145 | exit({unexpected_message, Other}) 146 | after TimeoutMillisec -> 147 | case rabbit_net:getstat(Sock, [StatName]) of 148 | {ok, [{StatName, NewStatVal}]} -> 149 | if NewStatVal =/= StatVal -> 150 | Recurse({NewStatVal, 0}); 151 | SameCount < Threshold -> 152 | Recurse({NewStatVal, SameCount + 1}); 153 | true -> 154 | case Handler() of 155 | stop -> ok; 156 | continue -> Recurse({NewStatVal, 0}) 157 | end 158 | end; 159 | {error, einval} -> 160 | %% the socket is dead, most likely because the 161 | %% connection is being shut down -> terminate 162 | ok; 163 | {error, Reason} -> 164 | exit({cannot_get_socket_stats, Reason}) 165 | end 166 | end. 167 | -------------------------------------------------------------------------------- /src/rabbit_msg_store_index.erl: -------------------------------------------------------------------------------- 1 | %% The contents of this file are subject to the Mozilla Public License 2 | %% Version 1.1 (the "License"); you may not use this file except in 3 | %% compliance with the License. You may obtain a copy of the License 4 | %% at http://www.mozilla.org/MPL/ 5 | %% 6 | %% Software distributed under the License is distributed on an "AS IS" 7 | %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 8 | %% the License for the specific language governing rights and 9 | %% limitations under the License. 10 | %% 11 | %% The Original Code is RabbitMQ. 12 | %% 13 | %% The Initial Developer of the Original Code is GoPivotal, Inc. 14 | %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved. 15 | %% 16 | 17 | -module(rabbit_msg_store_index). 18 | 19 | -include("rabbit_msg_store.hrl"). 20 | 21 | -ifdef(use_specs). 22 | 23 | -type(dir() :: any()). 24 | -type(index_state() :: any()). 25 | -type(keyvalue() :: any()). 26 | -type(fieldpos() :: non_neg_integer()). 27 | -type(fieldvalue() :: any()). 28 | 29 | -callback new(dir()) -> index_state(). 30 | -callback recover(dir()) -> rabbit_types:ok_or_error2(index_state(), any()). 31 | -callback lookup(rabbit_types:msg_id(), index_state()) -> ('not_found' | keyvalue()). 32 | -callback insert(keyvalue(), index_state()) -> 'ok'. 33 | -callback update(keyvalue(), index_state()) -> 'ok'. 34 | -callback update_fields(rabbit_types:msg_id(), ({fieldpos(), fieldvalue()} | 35 | [{fieldpos(), fieldvalue()}]), 36 | index_state()) -> 'ok'. 37 | -callback delete(rabbit_types:msg_id(), index_state()) -> 'ok'. 38 | -callback delete_object(keyvalue(), index_state()) -> 'ok'. 39 | -callback delete_by_file(fieldvalue(), index_state()) -> 'ok'. 40 | -callback terminate(index_state()) -> any(). 41 | 42 | -else. 43 | 44 | -export([behaviour_info/1]). 45 | 46 | behaviour_info(callbacks) -> 47 | [{new, 1}, 48 | {recover, 1}, 49 | {lookup, 2}, 50 | {insert, 2}, 51 | {update, 2}, 52 | {update_fields, 3}, 53 | {delete, 2}, 54 | {delete_by_file, 2}, 55 | {terminate, 1}]; 56 | behaviour_info(_Other) -> 57 | undefined. 58 | 59 | -endif. 60 | -------------------------------------------------------------------------------- /src/rabbit_net.erl: -------------------------------------------------------------------------------- 1 | %% The contents of this file are subject to the Mozilla Public License 2 | %% Version 1.1 (the "License"); you may not use this file except in 3 | %% compliance with the License. You may obtain a copy of the License 4 | %% at http://www.mozilla.org/MPL/ 5 | %% 6 | %% Software distributed under the License is distributed on an "AS IS" 7 | %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 8 | %% the License for the specific language governing rights and 9 | %% limitations under the License. 10 | %% 11 | %% The Original Code is RabbitMQ. 12 | %% 13 | %% The Initial Developer of the Original Code is GoPivotal, Inc. 14 | %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved. 15 | %% 16 | 17 | -module(rabbit_net). 18 | -include("rabbit.hrl"). 19 | 20 | -ifdef(define_tls_atom_version). 21 | %% In Erlang R16B03, tls_atom_version() is defined in ssl_internal.hrl, 22 | %% which is not included by ssl_api.hrl. Instead of including it here, 23 | %% we redefine it to avoid too much pollution. 24 | -type tls_atom_version() :: sslv3 | tlsv1 | 'tlsv1.1' | 'tlsv1.2'. 25 | -endif. 26 | 27 | -include_lib("ssl/src/ssl_api.hrl"). 28 | 29 | -export([is_ssl/1, ssl_info/1, controlling_process/2, getstat/2, 30 | recv/1, sync_recv/2, async_recv/3, port_command/2, getopts/2, 31 | setopts/2, send/2, close/1, fast_close/1, sockname/1, peername/1, 32 | peercert/1, connection_string/2, socket_ends/2, is_loopback/1, 33 | accept_ack/2]). 34 | 35 | %%--------------------------------------------------------------------------- 36 | 37 | -ifdef(use_specs). 38 | 39 | -export_type([socket/0]). 40 | 41 | -type(stat_option() :: 42 | 'recv_cnt' | 'recv_max' | 'recv_avg' | 'recv_oct' | 'recv_dvi' | 43 | 'send_cnt' | 'send_max' | 'send_avg' | 'send_oct' | 'send_pend'). 44 | -type(ok_val_or_error(A) :: rabbit_types:ok_or_error2(A, any())). 45 | -type(ok_or_any_error() :: rabbit_types:ok_or_error(any())). 46 | -type(socket() :: port() | ssl:sslsocket()). 47 | -type(opts() :: [{atom(), any()} | 48 | {raw, non_neg_integer(), non_neg_integer(), binary()}]). 49 | -type(host_or_ip() :: binary() | inet:ip_address()). 50 | -spec(is_ssl/1 :: (socket()) -> boolean()). 51 | -spec(ssl_info/1 :: (socket()) 52 | -> 'nossl' | ok_val_or_error( 53 | [{atom(), any()}])). 54 | -spec(controlling_process/2 :: (socket(), pid()) -> ok_or_any_error()). 55 | -spec(getstat/2 :: 56 | (socket(), [stat_option()]) 57 | -> ok_val_or_error([{stat_option(), integer()}])). 58 | -spec(recv/1 :: (socket()) -> 59 | {'data', [char()] | binary()} | 'closed' | 60 | rabbit_types:error(any()) | {'other', any()}). 61 | -spec(sync_recv/2 :: (socket(), integer()) -> rabbit_types:ok(binary()) | 62 | rabbit_types:error(any())). 63 | -spec(async_recv/3 :: 64 | (socket(), integer(), timeout()) -> rabbit_types:ok(any())). 65 | -spec(port_command/2 :: (socket(), iolist()) -> 'true'). 66 | -spec(getopts/2 :: (socket(), [atom() | {raw, 67 | non_neg_integer(), 68 | non_neg_integer(), 69 | non_neg_integer() | binary()}]) 70 | -> ok_val_or_error(opts())). 71 | -spec(setopts/2 :: (socket(), opts()) -> ok_or_any_error()). 72 | -spec(send/2 :: (socket(), binary() | iolist()) -> ok_or_any_error()). 73 | -spec(close/1 :: (socket()) -> ok_or_any_error()). 74 | -spec(fast_close/1 :: (socket()) -> ok_or_any_error()). 75 | -spec(sockname/1 :: 76 | (socket()) 77 | -> ok_val_or_error({inet:ip_address(), rabbit_networking:ip_port()})). 78 | -spec(peername/1 :: 79 | (socket()) 80 | -> ok_val_or_error({inet:ip_address(), rabbit_networking:ip_port()})). 81 | -spec(peercert/1 :: 82 | (socket()) 83 | -> 'nossl' | ok_val_or_error(rabbit_ssl:certificate())). 84 | -spec(connection_string/2 :: 85 | (socket(), 'inbound' | 'outbound') -> ok_val_or_error(string())). 86 | -spec(socket_ends/2 :: 87 | (socket(), 'inbound' | 'outbound') 88 | -> ok_val_or_error({host_or_ip(), rabbit_networking:ip_port(), 89 | host_or_ip(), rabbit_networking:ip_port()})). 90 | -spec(is_loopback/1 :: (socket() | inet:ip_address()) -> boolean()). 91 | -spec(accept_ack/2 :: (any(), socket()) -> ok). 92 | 93 | -endif. 94 | 95 | %%--------------------------------------------------------------------------- 96 | 97 | -define(SSL_CLOSE_TIMEOUT, 5000). 98 | 99 | -define(IS_SSL(Sock), is_record(Sock, sslsocket)). 100 | 101 | is_ssl(Sock) -> ?IS_SSL(Sock). 102 | 103 | %% Seems hackish. Is hackish. But the structure is stable and 104 | %% kept this way for backward compatibility reasons. We need 105 | %% it for two reasons: there are no ssl:getstat(Sock) function, 106 | %% and no ssl:close(Timeout) function. Both of them are being 107 | %% worked on as we speak. 108 | ssl_get_socket(Sock) -> 109 | element(2, element(2, Sock)). 110 | 111 | ssl_info(Sock) when ?IS_SSL(Sock) -> 112 | ssl_compat:connection_information(Sock); 113 | ssl_info(_Sock) -> 114 | nossl. 115 | 116 | controlling_process(Sock, Pid) when ?IS_SSL(Sock) -> 117 | ssl:controlling_process(Sock, Pid); 118 | controlling_process(Sock, Pid) when is_port(Sock) -> 119 | gen_tcp:controlling_process(Sock, Pid). 120 | 121 | getstat(Sock, Stats) when ?IS_SSL(Sock) -> 122 | inet:getstat(ssl_get_socket(Sock), Stats); 123 | getstat(Sock, Stats) when is_port(Sock) -> 124 | inet:getstat(Sock, Stats). 125 | 126 | recv(Sock) when ?IS_SSL(Sock) -> 127 | recv(Sock, {ssl, ssl_closed, ssl_error}); 128 | recv(Sock) when is_port(Sock) -> 129 | recv(Sock, {tcp, tcp_closed, tcp_error}). 130 | 131 | recv(S, {DataTag, ClosedTag, ErrorTag}) -> 132 | receive 133 | {DataTag, S, Data} -> {data, Data}; 134 | {ClosedTag, S} -> closed; 135 | {ErrorTag, S, Reason} -> {error, Reason}; 136 | Other -> {other, Other} 137 | end. 138 | 139 | sync_recv(Sock, Length) when ?IS_SSL(Sock) -> 140 | ssl:recv(Sock, Length); 141 | sync_recv(Sock, Length) -> 142 | gen_tcp:recv(Sock, Length). 143 | 144 | async_recv(Sock, Length, Timeout) when ?IS_SSL(Sock) -> 145 | Pid = self(), 146 | Ref = make_ref(), 147 | 148 | spawn(fun () -> Pid ! {inet_async, Sock, Ref, 149 | ssl:recv(Sock, Length, Timeout)} 150 | end), 151 | 152 | {ok, Ref}; 153 | async_recv(Sock, Length, infinity) when is_port(Sock) -> 154 | prim_inet:async_recv(Sock, Length, -1); 155 | async_recv(Sock, Length, Timeout) when is_port(Sock) -> 156 | prim_inet:async_recv(Sock, Length, Timeout). 157 | 158 | port_command(Sock, Data) when ?IS_SSL(Sock) -> 159 | case ssl:send(Sock, Data) of 160 | ok -> self() ! {inet_reply, Sock, ok}, 161 | true; 162 | {error, Reason} -> erlang:error(Reason) 163 | end; 164 | port_command(Sock, Data) when is_port(Sock) -> 165 | erlang:port_command(Sock, Data). 166 | 167 | getopts(Sock, Options) when ?IS_SSL(Sock) -> 168 | ssl:getopts(Sock, Options); 169 | getopts(Sock, Options) when is_port(Sock) -> 170 | inet:getopts(Sock, Options). 171 | 172 | setopts(Sock, Options) when ?IS_SSL(Sock) -> 173 | ssl:setopts(Sock, Options); 174 | setopts(Sock, Options) when is_port(Sock) -> 175 | inet:setopts(Sock, Options). 176 | 177 | send(Sock, Data) when ?IS_SSL(Sock) -> ssl:send(Sock, Data); 178 | send(Sock, Data) when is_port(Sock) -> gen_tcp:send(Sock, Data). 179 | 180 | close(Sock) when ?IS_SSL(Sock) -> ssl:close(Sock); 181 | close(Sock) when is_port(Sock) -> gen_tcp:close(Sock). 182 | 183 | fast_close(Sock) when ?IS_SSL(Sock) -> 184 | %% We cannot simply port_close the underlying tcp socket since the 185 | %% TLS protocol is quite insistent that a proper closing handshake 186 | %% should take place (see RFC 5245 s7.2.1). So we call ssl:close 187 | %% instead, but that can block for a very long time, e.g. when 188 | %% there is lots of pending output and there is tcp backpressure, 189 | %% or the ssl_connection process has entered the the 190 | %% workaround_transport_delivery_problems function during 191 | %% termination, which, inexplicably, does a gen_tcp:recv(Socket, 192 | %% 0), which may never return if the client doesn't send a FIN or 193 | %% that gets swallowed by the network. Since there is no timeout 194 | %% variant of ssl:close, we construct our own. 195 | {Pid, MRef} = spawn_monitor(fun () -> ssl:close(Sock) end), 196 | erlang:send_after(?SSL_CLOSE_TIMEOUT, self(), {Pid, ssl_close_timeout}), 197 | receive 198 | {Pid, ssl_close_timeout} -> 199 | erlang:demonitor(MRef, [flush]), 200 | exit(Pid, kill); 201 | {'DOWN', MRef, process, Pid, _Reason} -> 202 | ok 203 | end, 204 | catch port_close(ssl_get_socket(Sock)), 205 | ok; 206 | fast_close(Sock) when is_port(Sock) -> 207 | catch port_close(Sock), ok. 208 | 209 | sockname(Sock) when ?IS_SSL(Sock) -> ssl:sockname(Sock); 210 | sockname(Sock) when is_port(Sock) -> inet:sockname(Sock). 211 | 212 | peername(Sock) when ?IS_SSL(Sock) -> ssl:peername(Sock); 213 | peername(Sock) when is_port(Sock) -> inet:peername(Sock). 214 | 215 | peercert(Sock) when ?IS_SSL(Sock) -> ssl:peercert(Sock); 216 | peercert(Sock) when is_port(Sock) -> nossl. 217 | 218 | connection_string(Sock, Direction) -> 219 | case socket_ends(Sock, Direction) of 220 | {ok, {FromAddress, FromPort, ToAddress, ToPort}} -> 221 | {ok, rabbit_misc:format( 222 | "~s:~p -> ~s:~p", 223 | [maybe_ntoab(FromAddress), FromPort, 224 | maybe_ntoab(ToAddress), ToPort])}; 225 | Error -> 226 | Error 227 | end. 228 | 229 | socket_ends(Sock, Direction) -> 230 | {From, To} = sock_funs(Direction), 231 | case {From(Sock), To(Sock)} of 232 | {{ok, {FromAddress, FromPort}}, {ok, {ToAddress, ToPort}}} -> 233 | {ok, {rdns(FromAddress), FromPort, 234 | rdns(ToAddress), ToPort}}; 235 | {{error, _Reason} = Error, _} -> 236 | Error; 237 | {_, {error, _Reason} = Error} -> 238 | Error 239 | end. 240 | 241 | maybe_ntoab(Addr) when is_tuple(Addr) -> rabbit_misc:ntoab(Addr); 242 | maybe_ntoab(Host) -> Host. 243 | 244 | rdns(Addr) -> 245 | case application:get_env(rabbit, reverse_dns_lookups) of 246 | {ok, true} -> list_to_binary(rabbit_networking:tcp_host(Addr)); 247 | _ -> Addr 248 | end. 249 | 250 | sock_funs(inbound) -> {fun peername/1, fun sockname/1}; 251 | sock_funs(outbound) -> {fun sockname/1, fun peername/1}. 252 | 253 | is_loopback(Sock) when is_port(Sock) ; ?IS_SSL(Sock) -> 254 | case sockname(Sock) of 255 | {ok, {Addr, _Port}} -> is_loopback(Addr); 256 | {error, _} -> false 257 | end; 258 | %% We could parse the results of inet:getifaddrs() instead. But that 259 | %% would be more complex and less maybe Windows-compatible... 260 | is_loopback({127,_,_,_}) -> true; 261 | is_loopback({0,0,0,0,0,0,0,1}) -> true; 262 | is_loopback({0,0,0,0,0,65535,AB,CD}) -> is_loopback(ipv4(AB, CD)); 263 | is_loopback(_) -> false. 264 | 265 | ipv4(AB, CD) -> {AB bsr 8, AB band 255, CD bsr 8, CD band 255}. 266 | 267 | accept_ack(Ref, Sock) -> 268 | ok = ranch:accept_ack(Ref), 269 | case tune_buffer_size(Sock) of 270 | ok -> ok; 271 | {error, _} -> rabbit_net:fast_close(Sock), 272 | exit(normal) 273 | end, 274 | ok = file_handle_cache:obtain(). 275 | 276 | tune_buffer_size(Sock) -> 277 | case getopts(Sock, [sndbuf, recbuf, buffer]) of 278 | {ok, BufSizes} -> BufSz = lists:max([Sz || {_Opt, Sz} <- BufSizes]), 279 | setopts(Sock, [{buffer, BufSz}]); 280 | Error -> Error 281 | end. 282 | -------------------------------------------------------------------------------- /src/rabbit_nodes.erl: -------------------------------------------------------------------------------- 1 | %% The contents of this file are subject to the Mozilla Public License 2 | %% Version 1.1 (the "License"); you may not use this file except in 3 | %% compliance with the License. You may obtain a copy of the License 4 | %% at http://www.mozilla.org/MPL/ 5 | %% 6 | %% Software distributed under the License is distributed on an "AS IS" 7 | %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 8 | %% the License for the specific language governing rights and 9 | %% limitations under the License. 10 | %% 11 | %% The Original Code is RabbitMQ. 12 | %% 13 | %% The Initial Developer of the Original Code is GoPivotal, Inc. 14 | %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved. 15 | %% 16 | 17 | -module(rabbit_nodes). 18 | 19 | -export([names/1, diagnostics/1, make/1, parts/1, cookie_hash/0, 20 | is_running/2, is_process_running/2, 21 | cluster_name/0, set_cluster_name/1, ensure_epmd/0, 22 | all_running/0]). 23 | 24 | -include_lib("kernel/include/inet.hrl"). 25 | 26 | -define(EPMD_TIMEOUT, 30000). 27 | -define(TCP_DIAGNOSTIC_TIMEOUT, 5000). 28 | -define(ERROR_LOGGER_HANDLER, rabbit_error_logger_handler). 29 | 30 | %%---------------------------------------------------------------------------- 31 | %% Specs 32 | %%---------------------------------------------------------------------------- 33 | 34 | -ifdef(use_specs). 35 | 36 | -spec(names/1 :: (string()) -> rabbit_types:ok_or_error2( 37 | [{string(), integer()}], term())). 38 | -spec(diagnostics/1 :: ([node()]) -> string()). 39 | -spec(make/1 :: ({string(), string()} | string()) -> node()). 40 | -spec(parts/1 :: (node() | string()) -> {string(), string()}). 41 | -spec(cookie_hash/0 :: () -> string()). 42 | -spec(is_running/2 :: (node(), atom()) -> boolean()). 43 | -spec(is_process_running/2 :: (node(), atom()) -> boolean()). 44 | -spec(cluster_name/0 :: () -> binary()). 45 | -spec(set_cluster_name/1 :: (binary()) -> 'ok'). 46 | -spec(ensure_epmd/0 :: () -> 'ok'). 47 | -spec(all_running/0 :: () -> [node()]). 48 | 49 | -endif. 50 | 51 | %%---------------------------------------------------------------------------- 52 | 53 | names(Hostname) -> 54 | Self = self(), 55 | Ref = make_ref(), 56 | {Pid, MRef} = spawn_monitor( 57 | fun () -> Self ! {Ref, net_adm:names(Hostname)} end), 58 | timer:exit_after(?EPMD_TIMEOUT, Pid, timeout), 59 | receive 60 | {Ref, Names} -> erlang:demonitor(MRef, [flush]), 61 | Names; 62 | {'DOWN', MRef, process, Pid, Reason} -> {error, Reason} 63 | end. 64 | 65 | diagnostics(Nodes) -> 66 | verbose_erlang_distribution(true), 67 | NodeDiags = [{"~nDIAGNOSTICS~n===========~n~n" 68 | "attempted to contact: ~p~n", [Nodes]}] ++ 69 | [diagnostics_node(Node) || Node <- Nodes] ++ 70 | current_node_details(), 71 | verbose_erlang_distribution(false), 72 | rabbit_misc:format_many(lists:flatten(NodeDiags)). 73 | 74 | verbose_erlang_distribution(true) -> 75 | net_kernel:verbose(1), 76 | error_logger:add_report_handler(?ERROR_LOGGER_HANDLER); 77 | verbose_erlang_distribution(false) -> 78 | net_kernel:verbose(0), 79 | error_logger:delete_report_handler(?ERROR_LOGGER_HANDLER). 80 | 81 | current_node_details() -> 82 | [{"~ncurrent node details:~n- node name: ~w", [node()]}, 83 | case init:get_argument(home) of 84 | {ok, [[Home]]} -> {"- home dir: ~s", [Home]}; 85 | Other -> {"- no home dir: ~p", [Other]} 86 | end, 87 | {"- cookie hash: ~s", [cookie_hash()]}]. 88 | 89 | diagnostics_node(Node) -> 90 | {Name, Host} = parts(Node), 91 | [{"~s:", [Node]} | 92 | case names(Host) of 93 | {error, Reason} -> 94 | [{" * unable to connect to epmd (port ~s) on ~s: ~s~n", 95 | [epmd_port(), Host, rabbit_misc:format_inet_error(Reason)]}]; 96 | {ok, NamePorts} -> 97 | [{" * connected to epmd (port ~s) on ~s", 98 | [epmd_port(), Host]}] ++ 99 | case net_adm:ping(Node) of 100 | pong -> dist_working_diagnostics(Node); 101 | pang -> dist_broken_diagnostics(Name, Host, NamePorts) 102 | end 103 | end]. 104 | 105 | epmd_port() -> 106 | case init:get_argument(epmd_port) of 107 | {ok, [[Port | _] | _]} when is_list(Port) -> Port; 108 | error -> "4369" 109 | end. 110 | 111 | dist_working_diagnostics(Node) -> 112 | case rabbit:is_running(Node) of 113 | true -> [{" * node ~s up, 'rabbit' application running", [Node]}]; 114 | false -> [{" * node ~s up, 'rabbit' application not running~n" 115 | " * running applications on ~s: ~p~n" 116 | " * suggestion: start_app on ~s", 117 | [Node, Node, remote_apps(Node), Node]}] 118 | end. 119 | 120 | remote_apps(Node) -> 121 | %% We want a timeout here because really, we don't trust the node, 122 | %% the last thing we want to do is hang. 123 | case rpc:call(Node, application, which_applications, [5000]) of 124 | {badrpc, _} = E -> E; 125 | Apps -> [App || {App, _, _} <- Apps] 126 | end. 127 | 128 | dist_broken_diagnostics(Name, Host, NamePorts) -> 129 | case [{N, P} || {N, P} <- NamePorts, N =:= Name] of 130 | [] -> 131 | {SelfName, SelfHost} = parts(node()), 132 | Others = [list_to_atom(N) || {N, _} <- NamePorts, 133 | N =/= case SelfHost of 134 | Host -> SelfName; 135 | _ -> never_matches 136 | end], 137 | OthersDiag = case Others of 138 | [] -> [{" no other nodes on ~s", 139 | [Host]}]; 140 | _ -> [{" other nodes on ~s: ~p", 141 | [Host, Others]}] 142 | end, 143 | [{" * epmd reports: node '~s' not running at all", [Name]}, 144 | OthersDiag, {" * suggestion: start the node", []}]; 145 | [{Name, Port}] -> 146 | [{" * epmd reports node '~s' running on port ~b", [Name, Port]} | 147 | case diagnose_connect(Host, Port) of 148 | ok -> 149 | connection_succeeded_diagnostics(); 150 | {error, Reason} -> 151 | [{" * can't establish TCP connection, reason: ~s~n" 152 | " * suggestion: blocked by firewall?", 153 | [rabbit_misc:format_inet_error(Reason)]}] 154 | end] 155 | end. 156 | 157 | connection_succeeded_diagnostics() -> 158 | case gen_event:call(error_logger, ?ERROR_LOGGER_HANDLER, get_connection_report) of 159 | [] -> 160 | [{" * TCP connection succeeded but Erlang distribution " 161 | "failed~n" 162 | " * suggestion: hostname mismatch?~n" 163 | " * suggestion: is the cookie set correctly?~n" 164 | " * suggestion: is the Erlang distribution using TLS?", []}]; 165 | Report -> 166 | [{" * TCP connection succeeded but Erlang distribution " 167 | "failed~n", []}] 168 | ++ Report 169 | end. 170 | 171 | diagnose_connect(Host, Port) -> 172 | case inet:gethostbyname(Host) of 173 | {ok, #hostent{h_addrtype = Family}} -> 174 | case gen_tcp:connect(Host, Port, [Family], 175 | ?TCP_DIAGNOSTIC_TIMEOUT) of 176 | {ok, Socket} -> gen_tcp:close(Socket), 177 | ok; 178 | {error, _} = E -> E 179 | end; 180 | {error, _} = E -> 181 | E 182 | end. 183 | 184 | make({Prefix, Suffix}) -> list_to_atom(lists:append([Prefix, "@", Suffix])); 185 | make(NodeStr) -> make(parts(NodeStr)). 186 | 187 | parts(Node) when is_atom(Node) -> 188 | parts(atom_to_list(Node)); 189 | parts(NodeStr) -> 190 | case lists:splitwith(fun (E) -> E =/= $@ end, NodeStr) of 191 | {Prefix, []} -> {_, Suffix} = parts(node()), 192 | {Prefix, Suffix}; 193 | {Prefix, Suffix} -> {Prefix, tl(Suffix)} 194 | end. 195 | 196 | cookie_hash() -> 197 | base64:encode_to_string(erlang:md5(atom_to_list(erlang:get_cookie()))). 198 | 199 | is_running(Node, Application) -> 200 | case rpc:call(Node, rabbit_misc, which_applications, []) of 201 | {badrpc, _} -> false; 202 | Apps -> proplists:is_defined(Application, Apps) 203 | end. 204 | 205 | is_process_running(Node, Process) -> 206 | case rpc:call(Node, erlang, whereis, [Process]) of 207 | {badrpc, _} -> false; 208 | undefined -> false; 209 | P when is_pid(P) -> true 210 | end. 211 | 212 | cluster_name() -> 213 | rabbit_runtime_parameters:value_global( 214 | cluster_name, cluster_name_default()). 215 | 216 | cluster_name_default() -> 217 | {ID, _} = rabbit_nodes:parts(node()), 218 | {ok, Host} = inet:gethostname(), 219 | {ok, #hostent{h_name = FQDN}} = inet:gethostbyname(Host), 220 | list_to_binary(atom_to_list(rabbit_nodes:make({ID, FQDN}))). 221 | 222 | set_cluster_name(Name) -> 223 | rabbit_runtime_parameters:set_global(cluster_name, Name). 224 | 225 | ensure_epmd() -> 226 | {ok, Prog} = init:get_argument(progname), 227 | ID = rabbit_misc:random(1000000000), 228 | Port = open_port( 229 | {spawn_executable, os:find_executable(Prog)}, 230 | [{args, ["-sname", rabbit_misc:format("epmd-starter-~b", [ID]), 231 | "-noshell", "-eval", "halt()."]}, 232 | exit_status, stderr_to_stdout, use_stdio]), 233 | port_shutdown_loop(Port). 234 | 235 | port_shutdown_loop(Port) -> 236 | receive 237 | {Port, {exit_status, _Rc}} -> ok; 238 | {Port, _} -> port_shutdown_loop(Port) 239 | end. 240 | 241 | all_running() -> rabbit_mnesia:cluster_nodes(running). 242 | -------------------------------------------------------------------------------- /src/rabbit_password_hashing.erl: -------------------------------------------------------------------------------- 1 | %% The contents of this file are subject to the Mozilla Public License 2 | %% Version 1.1 (the "License"); you may not use this file except in 3 | %% compliance with the License. You may obtain a copy of the License 4 | %% at http://www.mozilla.org/MPL/ 5 | %% 6 | %% Software distributed under the License is distributed on an "AS IS" 7 | %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 8 | %% the License for the specific language governing rights and 9 | %% limitations under the License. 10 | %% 11 | %% The Original Code is RabbitMQ. 12 | %% 13 | %% The Initial Developer of the Original Code is GoPivotal, Inc. 14 | %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved. 15 | %% 16 | 17 | -module(rabbit_password_hashing). 18 | -include("rabbit.hrl"). 19 | 20 | -ifdef(use_specs). 21 | 22 | -callback hash(rabbit_types:password()) -> rabbit_types:password_hash(). 23 | 24 | -else. 25 | 26 | -export([behaviour_info/1]). 27 | 28 | behaviour_info(callbacks) -> 29 | [{hash, 1}]; 30 | behaviour_info(_Other) -> 31 | undefined. 32 | 33 | -endif. 34 | -------------------------------------------------------------------------------- /src/rabbit_policy_validator.erl: -------------------------------------------------------------------------------- 1 | %% The contents of this file are subject to the Mozilla Public License 2 | %% Version 1.1 (the "License"); you may not use this file except in 3 | %% compliance with the License. You may obtain a copy of the License 4 | %% at http://www.mozilla.org/MPL/ 5 | %% 6 | %% Software distributed under the License is distributed on an "AS IS" 7 | %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 8 | %% the License for the specific language governing rights and 9 | %% limitations under the License. 10 | %% 11 | %% The Original Code is RabbitMQ. 12 | %% 13 | %% The Initial Developer of the Original Code is GoPivotal, Inc. 14 | %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved. 15 | %% 16 | 17 | -module(rabbit_policy_validator). 18 | 19 | -ifdef(use_specs). 20 | 21 | -export_type([validate_results/0]). 22 | 23 | -type(validate_results() :: 24 | 'ok' | {error, string(), [term()]} | [validate_results()]). 25 | 26 | -callback validate_policy([{binary(), term()}]) -> validate_results(). 27 | 28 | -else. 29 | 30 | -export([behaviour_info/1]). 31 | 32 | behaviour_info(callbacks) -> 33 | [ 34 | {validate_policy, 1} 35 | ]; 36 | behaviour_info(_Other) -> 37 | undefined. 38 | 39 | -endif. 40 | -------------------------------------------------------------------------------- /src/rabbit_queue_collector.erl: -------------------------------------------------------------------------------- 1 | %% The contents of this file are subject to the Mozilla Public License 2 | %% Version 1.1 (the "License"); you may not use this file except in 3 | %% compliance with the License. You may obtain a copy of the License 4 | %% at http://www.mozilla.org/MPL/ 5 | %% 6 | %% Software distributed under the License is distributed on an "AS IS" 7 | %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 8 | %% the License for the specific language governing rights and 9 | %% limitations under the License. 10 | %% 11 | %% The Original Code is RabbitMQ. 12 | %% 13 | %% The Initial Developer of the Original Code is GoPivotal, Inc. 14 | %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved. 15 | %% 16 | 17 | -module(rabbit_queue_collector). 18 | 19 | %% Queue collector keeps track of exclusive queues and cleans them 20 | %% up e.g. when their connection is closed. 21 | 22 | -behaviour(gen_server). 23 | 24 | -export([start_link/1, register/2, delete_all/1]). 25 | 26 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2, 27 | terminate/2, code_change/3]). 28 | 29 | -record(state, {monitors, delete_from}). 30 | 31 | -include("rabbit.hrl"). 32 | 33 | %%---------------------------------------------------------------------------- 34 | 35 | -ifdef(use_specs). 36 | 37 | -spec(start_link/1 :: (rabbit_types:proc_name()) -> 38 | rabbit_types:ok_pid_or_error()). 39 | -spec(register/2 :: (pid(), pid()) -> 'ok'). 40 | -spec(delete_all/1 :: (pid()) -> 'ok'). 41 | 42 | -endif. 43 | 44 | %%---------------------------------------------------------------------------- 45 | 46 | start_link(ProcName) -> 47 | gen_server:start_link(?MODULE, [ProcName], []). 48 | 49 | register(CollectorPid, Q) -> 50 | gen_server:call(CollectorPid, {register, Q}, infinity). 51 | 52 | delete_all(CollectorPid) -> 53 | gen_server:call(CollectorPid, delete_all, infinity). 54 | 55 | %%---------------------------------------------------------------------------- 56 | 57 | init([ProcName]) -> 58 | ?store_proc_name(ProcName), 59 | {ok, #state{monitors = pmon:new(), delete_from = undefined}}. 60 | 61 | %%-------------------------------------------------------------------------- 62 | 63 | handle_call({register, QPid}, _From, 64 | State = #state{monitors = QMons, delete_from = Deleting}) -> 65 | case Deleting of 66 | undefined -> ok; 67 | _ -> ok = rabbit_amqqueue:delete_immediately([QPid]) 68 | end, 69 | {reply, ok, State#state{monitors = pmon:monitor(QPid, QMons)}}; 70 | 71 | handle_call(delete_all, From, State = #state{monitors = QMons, 72 | delete_from = undefined}) -> 73 | case pmon:monitored(QMons) of 74 | [] -> {reply, ok, State#state{delete_from = From}}; 75 | QPids -> ok = rabbit_amqqueue:delete_immediately(QPids), 76 | {noreply, State#state{delete_from = From}} 77 | end. 78 | 79 | handle_cast(Msg, State) -> 80 | {stop, {unhandled_cast, Msg}, State}. 81 | 82 | handle_info({'DOWN', _MRef, process, DownPid, _Reason}, 83 | State = #state{monitors = QMons, delete_from = Deleting}) -> 84 | QMons1 = pmon:erase(DownPid, QMons), 85 | case Deleting =/= undefined andalso pmon:is_empty(QMons1) of 86 | true -> gen_server:reply(Deleting, ok); 87 | false -> ok 88 | end, 89 | {noreply, State#state{monitors = QMons1}}. 90 | 91 | terminate(_Reason, _State) -> 92 | ok. 93 | 94 | code_change(_OldVsn, State, _Extra) -> 95 | {ok, State}. 96 | -------------------------------------------------------------------------------- /src/rabbit_queue_decorator.erl: -------------------------------------------------------------------------------- 1 | %% The contents of this file are subject to the Mozilla Public License 2 | %% Version 1.1 (the "License"); you may not use this file except in 3 | %% compliance with the License. You may obtain a copy of the License 4 | %% at http://www.mozilla.org/MPL/ 5 | %% 6 | %% Software distributed under the License is distributed on an "AS IS" 7 | %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 8 | %% the License for the specific language governing rights and 9 | %% limitations under the License. 10 | %% 11 | %% The Original Code is RabbitMQ. 12 | %% 13 | %% The Initial Developer of the Original Code is GoPivotal, Inc. 14 | %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved. 15 | %% 16 | 17 | -module(rabbit_queue_decorator). 18 | 19 | -include("rabbit.hrl"). 20 | 21 | -export([select/1, set/1, register/2, unregister/1]). 22 | 23 | %%---------------------------------------------------------------------------- 24 | 25 | -ifdef(use_specs). 26 | 27 | -callback startup(rabbit_types:amqqueue()) -> 'ok'. 28 | 29 | -callback shutdown(rabbit_types:amqqueue()) -> 'ok'. 30 | 31 | -callback policy_changed(rabbit_types:amqqueue(), rabbit_types:amqqueue()) -> 32 | 'ok'. 33 | 34 | -callback active_for(rabbit_types:amqqueue()) -> boolean(). 35 | 36 | %% called with Queue, MaxActivePriority, IsEmpty 37 | -callback consumer_state_changed( 38 | rabbit_types:amqqueue(), integer(), boolean()) -> 'ok'. 39 | 40 | -else. 41 | 42 | -export([behaviour_info/1]). 43 | 44 | behaviour_info(callbacks) -> 45 | [{startup, 1}, {shutdown, 1}, {policy_changed, 2}, 46 | {active_for, 1}, {consumer_state_changed, 3}]; 47 | behaviour_info(_Other) -> 48 | undefined. 49 | 50 | -endif. 51 | 52 | %%---------------------------------------------------------------------------- 53 | 54 | select(Modules) -> 55 | [M || M <- Modules, code:which(M) =/= non_existing]. 56 | 57 | set(Q) -> Q#amqqueue{decorators = [D || D <- list(), D:active_for(Q)]}. 58 | 59 | list() -> [M || {_, M} <- rabbit_registry:lookup_all(queue_decorator)]. 60 | 61 | register(TypeName, ModuleName) -> 62 | rabbit_registry:register(queue_decorator, TypeName, ModuleName), 63 | [maybe_recover(Q) || Q <- rabbit_amqqueue:list()], 64 | ok. 65 | 66 | unregister(TypeName) -> 67 | rabbit_registry:unregister(queue_decorator, TypeName), 68 | [maybe_recover(Q) || Q <- rabbit_amqqueue:list()], 69 | ok. 70 | 71 | maybe_recover(Q = #amqqueue{name = Name, 72 | decorators = Decs}) -> 73 | #amqqueue{decorators = Decs1} = set(Q), 74 | Old = lists:sort(select(Decs)), 75 | New = lists:sort(select(Decs1)), 76 | case New of 77 | Old -> ok; 78 | _ -> [M:startup(Q) || M <- New -- Old], 79 | rabbit_amqqueue:update_decorators(Name) 80 | end. 81 | -------------------------------------------------------------------------------- /src/rabbit_queue_master_locator.erl: -------------------------------------------------------------------------------- 1 | %% The contents of this file are subject to the Mozilla Public License 2 | %% Version 1.1 (the "License"); you may not use this file except in 3 | %% compliance with the License. You may obtain a copy of the License at 4 | %% http://www.mozilla.org/MPL/ 5 | %% 6 | %% Software distributed under the License is distributed on an "AS IS" 7 | %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the 8 | %% License for the specific language governing rights and limitations 9 | %% under the License. 10 | %% 11 | %% The Original Code is RabbitMQ. 12 | %% 13 | %% The Initial Developer of the Original Code is GoPivotal, Inc. 14 | %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved. 15 | %% 16 | 17 | -module(rabbit_queue_master_locator). 18 | 19 | -ifdef(use_specs). 20 | 21 | -callback description() -> [proplists:property()]. 22 | -callback queue_master_location(rabbit_types:amqqueue()) -> 23 | {'ok', node()} | {'error', term()}. 24 | 25 | -else. 26 | 27 | -export([behaviour_info/1]). 28 | behaviour_info(callbacks) -> 29 | [{description, 0}, 30 | {queue_master_location, 1}]; 31 | behaviour_info(_Other) -> 32 | undefined. 33 | 34 | -endif. 35 | -------------------------------------------------------------------------------- /src/rabbit_runtime_parameter.erl: -------------------------------------------------------------------------------- 1 | %% The contents of this file are subject to the Mozilla Public License 2 | %% Version 1.1 (the "License"); you may not use this file except in 3 | %% compliance with the License. You may obtain a copy of the License 4 | %% at http://www.mozilla.org/MPL/ 5 | %% 6 | %% Software distributed under the License is distributed on an "AS IS" 7 | %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 8 | %% the License for the specific language governing rights and 9 | %% limitations under the License. 10 | %% 11 | %% The Original Code is RabbitMQ. 12 | %% 13 | %% The Initial Developer of the Original Code is GoPivotal, Inc. 14 | %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved. 15 | %% 16 | 17 | -module(rabbit_runtime_parameter). 18 | 19 | -ifdef(use_specs). 20 | 21 | -type(validate_results() :: 22 | 'ok' | {error, string(), [term()]} | [validate_results()]). 23 | 24 | -callback validate(rabbit_types:vhost(), binary(), binary(), 25 | term(), rabbit_types:user()) -> validate_results(). 26 | -callback notify(rabbit_types:vhost(), binary(), binary(), term()) -> 'ok'. 27 | -callback notify_clear(rabbit_types:vhost(), binary(), binary()) -> 'ok'. 28 | 29 | -else. 30 | 31 | -export([behaviour_info/1]). 32 | 33 | behaviour_info(callbacks) -> 34 | [ 35 | {validate, 5}, 36 | {notify, 4}, 37 | {notify_clear, 3} 38 | ]; 39 | behaviour_info(_Other) -> 40 | undefined. 41 | 42 | -endif. 43 | -------------------------------------------------------------------------------- /src/rabbit_types.erl: -------------------------------------------------------------------------------- 1 | %% The contents of this file are subject to the Mozilla Public License 2 | %% Version 1.1 (the "License"); you may not use this file except in 3 | %% compliance with the License. You may obtain a copy of the License 4 | %% at http://www.mozilla.org/MPL/ 5 | %% 6 | %% Software distributed under the License is distributed on an "AS IS" 7 | %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 8 | %% the License for the specific language governing rights and 9 | %% limitations under the License. 10 | %% 11 | %% The Original Code is RabbitMQ. 12 | %% 13 | %% The Initial Developer of the Original Code is GoPivotal, Inc. 14 | %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved. 15 | %% 16 | 17 | -module(rabbit_types). 18 | 19 | -include("rabbit.hrl"). 20 | 21 | -ifdef(use_specs). 22 | 23 | -export_type([maybe/1, info/0, infos/0, info_key/0, info_keys/0, 24 | message/0, msg_id/0, basic_message/0, 25 | delivery/0, content/0, decoded_content/0, undecoded_content/0, 26 | unencoded_content/0, encoded_content/0, message_properties/0, 27 | vhost/0, ctag/0, amqp_error/0, r/1, r2/2, r3/3, listener/0, 28 | binding/0, binding_source/0, binding_destination/0, 29 | amqqueue/0, exchange/0, 30 | connection/0, protocol/0, auth_user/0, user/0, internal_user/0, 31 | username/0, password/0, password_hash/0, 32 | ok/1, error/1, ok_or_error/1, ok_or_error2/2, ok_pid_or_error/0, 33 | channel_exit/0, connection_exit/0, mfargs/0, proc_name/0, 34 | proc_type_and_name/0, timestamp/0]). 35 | 36 | -type(maybe(T) :: T | 'none'). 37 | -type(timestamp() :: {non_neg_integer(), non_neg_integer(), non_neg_integer()}). 38 | -type(vhost() :: binary()). 39 | -type(ctag() :: binary()). 40 | 41 | %% TODO: make this more precise by tying specific class_ids to 42 | %% specific properties 43 | -type(undecoded_content() :: 44 | #content{class_id :: rabbit_framing:amqp_class_id(), 45 | properties :: 'none', 46 | properties_bin :: binary(), 47 | payload_fragments_rev :: [binary()]} | 48 | #content{class_id :: rabbit_framing:amqp_class_id(), 49 | properties :: rabbit_framing:amqp_property_record(), 50 | properties_bin :: 'none', 51 | payload_fragments_rev :: [binary()]}). 52 | -type(unencoded_content() :: undecoded_content()). 53 | -type(decoded_content() :: 54 | #content{class_id :: rabbit_framing:amqp_class_id(), 55 | properties :: rabbit_framing:amqp_property_record(), 56 | properties_bin :: maybe(binary()), 57 | payload_fragments_rev :: [binary()]}). 58 | -type(encoded_content() :: 59 | #content{class_id :: rabbit_framing:amqp_class_id(), 60 | properties :: maybe(rabbit_framing:amqp_property_record()), 61 | properties_bin :: binary(), 62 | payload_fragments_rev :: [binary()]}). 63 | -type(content() :: undecoded_content() | decoded_content()). 64 | -type(msg_id() :: rabbit_guid:guid()). 65 | -type(basic_message() :: 66 | #basic_message{exchange_name :: rabbit_exchange:name(), 67 | routing_keys :: [rabbit_router:routing_key()], 68 | content :: content(), 69 | id :: msg_id(), 70 | is_persistent :: boolean()}). 71 | -type(message() :: basic_message()). 72 | -type(delivery() :: 73 | #delivery{mandatory :: boolean(), 74 | sender :: pid(), 75 | message :: message()}). 76 | -type(message_properties() :: 77 | #message_properties{expiry :: pos_integer() | 'undefined', 78 | needs_confirming :: boolean()}). 79 | 80 | -type(info_key() :: atom()). 81 | -type(info_keys() :: [info_key()]). 82 | 83 | -type(info() :: {info_key(), any()}). 84 | -type(infos() :: [info()]). 85 | 86 | -type(amqp_error() :: 87 | #amqp_error{name :: rabbit_framing:amqp_exception(), 88 | explanation :: string(), 89 | method :: rabbit_framing:amqp_method_name()}). 90 | 91 | -type(r(Kind) :: 92 | r2(vhost(), Kind)). 93 | -type(r2(VirtualHost, Kind) :: 94 | r3(VirtualHost, Kind, rabbit_misc:resource_name())). 95 | -type(r3(VirtualHost, Kind, Name) :: 96 | #resource{virtual_host :: VirtualHost, 97 | kind :: Kind, 98 | name :: Name}). 99 | 100 | -type(listener() :: 101 | #listener{node :: node(), 102 | protocol :: atom(), 103 | host :: rabbit_networking:hostname(), 104 | port :: rabbit_networking:ip_port()}). 105 | 106 | -type(binding_source() :: rabbit_exchange:name()). 107 | -type(binding_destination() :: rabbit_amqqueue:name() | rabbit_exchange:name()). 108 | 109 | -type(binding() :: 110 | #binding{source :: rabbit_exchange:name(), 111 | destination :: binding_destination(), 112 | key :: rabbit_binding:key(), 113 | args :: rabbit_framing:amqp_table()}). 114 | 115 | -type(amqqueue() :: 116 | #amqqueue{name :: rabbit_amqqueue:name(), 117 | durable :: boolean(), 118 | auto_delete :: boolean(), 119 | exclusive_owner :: rabbit_types:maybe(pid()), 120 | arguments :: rabbit_framing:amqp_table(), 121 | pid :: rabbit_types:maybe(pid()), 122 | slave_pids :: [pid()]}). 123 | 124 | -type(exchange() :: 125 | #exchange{name :: rabbit_exchange:name(), 126 | type :: rabbit_exchange:type(), 127 | durable :: boolean(), 128 | auto_delete :: boolean(), 129 | arguments :: rabbit_framing:amqp_table()}). 130 | 131 | -type(connection() :: pid()). 132 | 133 | -type(protocol() :: rabbit_framing:protocol()). 134 | 135 | -type(auth_user() :: 136 | #auth_user{username :: username(), 137 | tags :: [atom()], 138 | impl :: any()}). 139 | 140 | -type(user() :: 141 | #user{username :: username(), 142 | tags :: [atom()], 143 | authz_backends :: [{atom(), any()}]}). 144 | 145 | -type(internal_user() :: 146 | #internal_user{username :: username(), 147 | password_hash :: password_hash(), 148 | tags :: [atom()]}). 149 | 150 | -type(username() :: binary()). 151 | -type(password() :: binary()). 152 | -type(password_hash() :: binary()). 153 | 154 | -type(ok(A) :: {'ok', A}). 155 | -type(error(A) :: {'error', A}). 156 | -type(ok_or_error(A) :: 'ok' | error(A)). 157 | -type(ok_or_error2(A, B) :: ok(A) | error(B)). 158 | -type(ok_pid_or_error() :: ok_or_error2(pid(), any())). 159 | 160 | -type(channel_exit() :: no_return()). 161 | -type(connection_exit() :: no_return()). 162 | 163 | -type(mfargs() :: {atom(), atom(), [any()]}). 164 | 165 | -type(proc_name() :: term()). 166 | -type(proc_type_and_name() :: {atom(), proc_name()}). 167 | 168 | -endif. % use_specs 169 | -------------------------------------------------------------------------------- /src/ssl_compat.erl: -------------------------------------------------------------------------------- 1 | %% The contents of this file are subject to the Mozilla Public License 2 | %% Version 1.1 (the "License"); you may not use this file except in 3 | %% compliance with the License. You may obtain a copy of the License 4 | %% at http://www.mozilla.org/MPL/ 5 | %% 6 | %% Software distributed under the License is distributed on an "AS IS" 7 | %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 8 | %% the License for the specific language governing rights and 9 | %% limitations under the License. 10 | %% 11 | %% The Original Code is RabbitMQ. 12 | %% 13 | %% The Initial Developer of the Original Code is GoPivotal, Inc. 14 | %% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved. 15 | %% 16 | 17 | -module(ssl_compat). 18 | 19 | %% We don't want warnings about the use of erlang:now/0 in 20 | %% this module. 21 | -compile(nowarn_deprecated_function). 22 | 23 | %% Declare versioned functions to allow dynamic code loading, 24 | %% depending on the Erlang version running. See 'code_version.erl' for details 25 | -erlang_version_support( 26 | [{18, [{connection_information, 1, connection_information_pre_18, 27 | connection_information_post_18}, 28 | {connection_information, 2, connection_information_pre_18, 29 | connection_information_post_18}]} 30 | ]). 31 | 32 | -export([connection_information/1, 33 | connection_information_pre_18/1, 34 | connection_information_post_18/1, 35 | connection_information/2, 36 | connection_information_pre_18/2, 37 | connection_information_post_18/2]). 38 | 39 | connection_information(SslSocket) -> 40 | code_version:update(?MODULE), 41 | ssl_compat:connection_information(SslSocket). 42 | 43 | connection_information_post_18(SslSocket) -> 44 | ssl:connection_information(SslSocket). 45 | 46 | connection_information_pre_18(SslSocket) -> 47 | case ssl:connection_info(SslSocket) of 48 | {ok, {ProtocolVersion, CipherSuite}} -> 49 | {ok, [{protocol, ProtocolVersion}, 50 | {cipher_suite, CipherSuite}]}; 51 | {error, Reason} -> 52 | {error, Reason} 53 | end. 54 | 55 | connection_information(SslSocket, Items) -> 56 | code_version:update(?MODULE), 57 | ssl_compat:connection_information(SslSocket, Items). 58 | 59 | connection_information_post_18(SslSocket, Items) -> 60 | ssl:connection_information(SslSocket, Items). 61 | 62 | connection_information_pre_18(SslSocket, Items) -> 63 | WantProtocolVersion = lists:member(protocol, Items), 64 | WantCipherSuite = lists:member(cipher_suite, Items), 65 | if 66 | WantProtocolVersion orelse WantCipherSuite -> 67 | case ssl:connection_info(SslSocket) of 68 | {ok, {ProtocolVersion, CipherSuite}} -> 69 | filter_information_items(ProtocolVersion, 70 | CipherSuite, 71 | Items, 72 | []); 73 | {error, Reason} -> 74 | {error, Reason} 75 | end; 76 | true -> 77 | {ok, []} 78 | end. 79 | 80 | filter_information_items(ProtocolVersion, CipherSuite, [protocol | Rest], 81 | Result) -> 82 | filter_information_items(ProtocolVersion, CipherSuite, Rest, 83 | [{protocol, ProtocolVersion} | Result]); 84 | filter_information_items(ProtocolVersion, CipherSuite, [cipher_suite | Rest], 85 | Result) -> 86 | filter_information_items(ProtocolVersion, CipherSuite, Rest, 87 | [{cipher_suite, CipherSuite} | Result]); 88 | filter_information_items(ProtocolVersion, CipherSuite, [_ | Rest], 89 | Result) -> 90 | filter_information_items(ProtocolVersion, CipherSuite, Rest, Result); 91 | filter_information_items(_ProtocolVersion, _CipherSuite, [], Result) -> 92 | {ok, lists:reverse(Result)}. 93 | -------------------------------------------------------------------------------- /src/time_compat.erl: -------------------------------------------------------------------------------- 1 | %% 2 | %% %CopyrightBegin% 3 | %% 4 | %% Copyright Ericsson AB 2014-2015. All Rights Reserved. 5 | %% 6 | %% Licensed under the Apache License, Version 2.0 (the "License"); 7 | %% you may not use this file except in compliance with the License. 8 | %% You may obtain a copy of the License at 9 | %% 10 | %% http://www.apache.org/licenses/LICENSE-2.0 11 | %% 12 | %% Unless required by applicable law or agreed to in writing, software 13 | %% distributed under the License is distributed on an "AS IS" BASIS, 14 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | %% See the License for the specific language governing permissions and 16 | %% limitations under the License. 17 | %% 18 | %% %CopyrightEnd% 19 | %% 20 | 21 | %% 22 | %% If your code need to be able to execute on ERTS versions both 23 | %% earlier and later than 7.0, the best approach is to use the new 24 | %% time API introduced in ERTS 7.0 and implement a fallback 25 | %% solution using the old primitives to be used on old ERTS 26 | %% versions. This way your code can automatically take advantage 27 | %% of the improvements in the API when available. This is an 28 | %% example of how to implement such an API, but it can be used 29 | %% as is if you want to. Just add (a preferrably renamed version of) 30 | %% this module to your project, and call the API via this module 31 | %% instead of calling the BIFs directly. 32 | %% 33 | 34 | -module(time_compat). 35 | 36 | %% We don't want warnings about the use of erlang:now/0 in 37 | %% this module. 38 | -compile(nowarn_deprecated_function). 39 | %% 40 | %% We don't use 41 | %% -compile({nowarn_deprecated_function, [{erlang, now, 0}]}). 42 | %% since this will produce warnings when compiled on systems 43 | %% where it has not yet been deprecated. 44 | %% 45 | 46 | %% Declare versioned functions to allow dynamic code loading, 47 | %% depending on the Erlang version running. See 'code_version.erl' for details 48 | -erlang_version_support( 49 | [{18, 50 | [{monotonic_time, 0, monotonic_time_pre_18, monotonic_time_post_18}, 51 | {monotonic_time, 1, monotonic_time_pre_18, monotonic_time_post_18}, 52 | {erlang_system_time, 0, erlang_system_time_pre_18, erlang_system_time_post_18}, 53 | {erlang_system_time, 1, erlang_system_time_pre_18, erlang_system_time_post_18}, 54 | {os_system_time, 0, os_system_time_pre_18, os_system_time_post_18}, 55 | {os_system_time, 1, os_system_time_pre_18, os_system_time_post_18}, 56 | {time_offset, 0, time_offset_pre_18, time_offset_post_18}, 57 | {time_offset, 1, time_offset_pre_18, time_offset_post_18}, 58 | {convert_time_unit, 3, convert_time_unit_pre_18, convert_time_unit_post_18}, 59 | {timestamp, 0, timestamp_pre_18, timestamp_post_18}, 60 | {unique_integer, 0, unique_integer_pre_18, unique_integer_post_18}, 61 | {unique_integer, 1, unique_integer_pre_18, unique_integer_post_18}]} 62 | ]). 63 | 64 | -export([monotonic_time/0, 65 | monotonic_time_pre_18/0, 66 | monotonic_time_post_18/0, 67 | monotonic_time/1, 68 | monotonic_time_pre_18/1, 69 | monotonic_time_post_18/1, 70 | erlang_system_time/0, 71 | erlang_system_time_pre_18/0, 72 | erlang_system_time_post_18/0, 73 | erlang_system_time/1, 74 | erlang_system_time_pre_18/1, 75 | erlang_system_time_post_18/1, 76 | os_system_time/0, 77 | os_system_time_pre_18/0, 78 | os_system_time_post_18/0, 79 | os_system_time/1, 80 | os_system_time_pre_18/1, 81 | os_system_time_post_18/1, 82 | time_offset/0, 83 | time_offset_pre_18/0, 84 | time_offset_post_18/0, 85 | time_offset/1, 86 | time_offset_pre_18/1, 87 | time_offset_post_18/1, 88 | convert_time_unit/3, 89 | convert_time_unit_pre_18/3, 90 | convert_time_unit_post_18/3, 91 | timestamp/0, 92 | timestamp_pre_18/0, 93 | timestamp_post_18/0, 94 | unique_integer/0, 95 | unique_integer_pre_18/0, 96 | unique_integer_post_18/0, 97 | unique_integer/1, 98 | unique_integer_pre_18/1, 99 | unique_integer_post_18/1, 100 | monitor/2, 101 | system_info/1, 102 | system_flag/2]). 103 | 104 | monotonic_time() -> 105 | code_version:update(?MODULE), 106 | time_compat:monotonic_time(). 107 | 108 | monotonic_time_post_18() -> 109 | erlang:monotonic_time(). 110 | 111 | monotonic_time_pre_18() -> 112 | erlang_system_time_fallback(). 113 | 114 | monotonic_time(Unit) -> 115 | code_version:update(?MODULE), 116 | time_compat:monotonic_time(Unit). 117 | 118 | monotonic_time_post_18(Unit) -> 119 | erlang:monotonic_time(Unit). 120 | 121 | monotonic_time_pre_18(Unit) -> 122 | %% Use Erlang system time as monotonic time 123 | STime = erlang_system_time_fallback(), 124 | convert_time_unit_fallback(STime, native, Unit). 125 | 126 | erlang_system_time() -> 127 | code_version:update(?MODULE), 128 | time_compat:erlang_system_time(). 129 | 130 | erlang_system_time_post_18() -> 131 | erlang:system_time(). 132 | 133 | erlang_system_time_pre_18() -> 134 | erlang_system_time_fallback(). 135 | 136 | erlang_system_time(Unit) -> 137 | code_version:update(?MODULE), 138 | time_compat:erlang_system_time(Unit). 139 | 140 | erlang_system_time_post_18(Unit) -> 141 | erlang:system_time(Unit). 142 | 143 | erlang_system_time_pre_18(Unit) -> 144 | STime = erlang_system_time_fallback(), 145 | convert_time_unit_fallback(STime, native, Unit). 146 | 147 | os_system_time() -> 148 | code_version:update(?MODULE), 149 | time_compat:os_system_time(). 150 | 151 | os_system_time_post_18() -> 152 | os:system_time(). 153 | 154 | os_system_time_pre_18() -> 155 | os_system_time_fallback(). 156 | 157 | os_system_time(Unit) -> 158 | code_version:update(?MODULE), 159 | time_compat:os_system_time(Unit). 160 | 161 | os_system_time_post_18(Unit) -> 162 | os:system_time(Unit). 163 | 164 | os_system_time_pre_18(Unit) -> 165 | STime = os_system_time_fallback(), 166 | convert_time_unit_fallback(STime, native, Unit). 167 | 168 | time_offset() -> 169 | code_version:update(?MODULE), 170 | time_compat:time_offset(). 171 | 172 | time_offset_post_18() -> 173 | erlang:time_offset(). 174 | 175 | time_offset_pre_18() -> 176 | %% Erlang system time and Erlang monotonic 177 | %% time are always aligned 178 | 0. 179 | 180 | time_offset(Unit) -> 181 | code_version:update(?MODULE), 182 | time_compat:time_offset(Unit). 183 | 184 | time_offset_post_18(Unit) -> 185 | erlang:time_offset(Unit). 186 | 187 | time_offset_pre_18(Unit) -> 188 | _ = integer_time_unit(Unit), 189 | %% Erlang system time and Erlang monotonic 190 | %% time are always aligned 191 | 0. 192 | 193 | convert_time_unit(Time, FromUnit, ToUnit) -> 194 | code_version:update(?MODULE), 195 | time_compat:convert_time_unit(Time, FromUnit, ToUnit). 196 | 197 | convert_time_unit_post_18(Time, FromUnit, ToUnit) -> 198 | try 199 | erlang:convert_time_unit(Time, FromUnit, ToUnit) 200 | catch 201 | error:Error -> 202 | erlang:error(Error, [Time, FromUnit, ToUnit]) 203 | end. 204 | 205 | convert_time_unit_pre_18(Time, FromUnit, ToUnit) -> 206 | try 207 | convert_time_unit_fallback(Time, FromUnit, ToUnit) 208 | catch 209 | _:_ -> 210 | erlang:error(badarg, [Time, FromUnit, ToUnit]) 211 | end. 212 | 213 | timestamp() -> 214 | code_version:update(?MODULE), 215 | time_compat:timestamp(). 216 | 217 | timestamp_post_18() -> 218 | erlang:timestamp(). 219 | 220 | timestamp_pre_18() -> 221 | erlang:now(). 222 | 223 | unique_integer() -> 224 | code_version:update(?MODULE), 225 | time_compat:unique_integer(). 226 | 227 | unique_integer_post_18() -> 228 | erlang:unique_integer(). 229 | 230 | unique_integer_pre_18() -> 231 | {MS, S, US} = erlang:now(), 232 | (MS*1000000+S)*1000000+US. 233 | 234 | unique_integer(Modifiers) -> 235 | code_version:update(?MODULE), 236 | time_compat:unique_integer(Modifiers). 237 | 238 | unique_integer_post_18(Modifiers) -> 239 | erlang:unique_integer(Modifiers). 240 | 241 | unique_integer_pre_18(Modifiers) -> 242 | case is_valid_modifier_list(Modifiers) of 243 | true -> 244 | %% now() converted to an integer 245 | %% fullfill the requirements of 246 | %% all modifiers: unique, positive, 247 | %% and monotonic... 248 | {MS, S, US} = erlang:now(), 249 | (MS*1000000+S)*1000000+US; 250 | false -> 251 | erlang:error(badarg, [Modifiers]) 252 | end. 253 | 254 | monitor(Type, Item) -> 255 | try 256 | erlang:monitor(Type, Item) 257 | catch 258 | error:Error -> 259 | case {Error, Type, Item} of 260 | {badarg, time_offset, clock_service} -> 261 | %% Time offset is final and will never change. 262 | %% Return a dummy reference, there will never 263 | %% be any need for 'CHANGE' messages... 264 | make_ref(); 265 | _ -> 266 | erlang:error(Error, [Type, Item]) 267 | end 268 | end. 269 | 270 | system_info(Item) -> 271 | try 272 | erlang:system_info(Item) 273 | catch 274 | error:badarg -> 275 | case Item of 276 | time_correction -> 277 | case erlang:system_info(tolerant_timeofday) of 278 | enabled -> true; 279 | disabled -> false 280 | end; 281 | time_warp_mode -> 282 | no_time_warp; 283 | time_offset -> 284 | final; 285 | NotSupArg when NotSupArg == os_monotonic_time_source; 286 | NotSupArg == os_system_time_source; 287 | NotSupArg == start_time; 288 | NotSupArg == end_time -> 289 | %% Cannot emulate this... 290 | erlang:error(notsup, [NotSupArg]); 291 | _ -> 292 | erlang:error(badarg, [Item]) 293 | end; 294 | error:Error -> 295 | erlang:error(Error, [Item]) 296 | end. 297 | 298 | system_flag(Flag, Value) -> 299 | try 300 | erlang:system_flag(Flag, Value) 301 | catch 302 | error:Error -> 303 | case {Error, Flag, Value} of 304 | {badarg, time_offset, finalize} -> 305 | %% Time offset is final 306 | final; 307 | _ -> 308 | erlang:error(Error, [Flag, Value]) 309 | end 310 | end. 311 | 312 | %% 313 | %% Internal functions 314 | %% 315 | 316 | integer_time_unit(native) -> 1000*1000; 317 | integer_time_unit(nano_seconds) -> 1000*1000*1000; 318 | integer_time_unit(micro_seconds) -> 1000*1000; 319 | integer_time_unit(milli_seconds) -> 1000; 320 | integer_time_unit(seconds) -> 1; 321 | integer_time_unit(I) when is_integer(I), I > 0 -> I; 322 | integer_time_unit(BadRes) -> erlang:error(badarg, [BadRes]). 323 | 324 | erlang_system_time_fallback() -> 325 | {MS, S, US} = erlang:now(), 326 | (MS*1000000+S)*1000000+US. 327 | 328 | os_system_time_fallback() -> 329 | {MS, S, US} = os:timestamp(), 330 | (MS*1000000+S)*1000000+US. 331 | 332 | convert_time_unit_fallback(Time, FromUnit, ToUnit) -> 333 | FU = integer_time_unit(FromUnit), 334 | TU = integer_time_unit(ToUnit), 335 | case Time < 0 of 336 | true -> TU*Time - (FU - 1); 337 | false -> TU*Time 338 | end div FU. 339 | 340 | is_valid_modifier_list([positive|Ms]) -> 341 | is_valid_modifier_list(Ms); 342 | is_valid_modifier_list([monotonic|Ms]) -> 343 | is_valid_modifier_list(Ms); 344 | is_valid_modifier_list([]) -> 345 | true; 346 | is_valid_modifier_list(_) -> 347 | false. 348 | --------------------------------------------------------------------------------