This commit is contained in:
Daniil Fedotov 2016-05-16 15:28:28 +01:00
commit a8b2727ac3
11 changed files with 7400 additions and 0 deletions

View File

@ -0,0 +1,12 @@
.sw?
.*.sw?
*.beam
/.erlang.mk/
/cover/
/deps/
/doc/
/ebin/
/logs/
/plugins/
/rabbitmq_auth_backend_cache.d

View File

@ -0,0 +1,15 @@
PROJECT = rabbitmq_auth_backend_cache
DEPS = rabbit_common
DEP_PLUGINS = rabbit_common/mk/rabbitmq-plugin.mk
# FIXME: Use erlang.mk patched for RabbitMQ, while waiting for PRs to be
# reviewed and merged.
ERLANG_MK_REPO = https://github.com/rabbitmq/erlang.mk.git
ERLANG_MK_COMMIT = rabbitmq-tmp
include rabbitmq-components.mk
include erlang.mk

View File

@ -0,0 +1,43 @@
# Do *not* comment or remove core modules
# unless you know what you are doing.
#
# Feel free to comment plugins out however.
# Core modules.
core/core
index/*
core/index
core/deps
# Plugins that must run before Erlang code gets compiled.
plugins/erlydtl
plugins/protobuffs
# Core modules, continued.
core/erlc
core/docs
core/rel
core/test
core/compat
# Plugins.
plugins/asciidoc
plugins/bootstrap
plugins/c_src
plugins/ci
plugins/ct
plugins/dialyzer
plugins/edoc
plugins/elvis
plugins/escript
# plugins/eunit
plugins/relx
plugins/shell
plugins/triq
plugins/xref
# Plugins enhancing the functionality of other plugins.
plugins/cover
# Core modules which can use variables from plugins.
core/deps-tools

6589
deps/rabbitmq_auth_backend_cache/erlang.mk vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,335 @@
ifeq ($(.DEFAULT_GOAL),)
# Define default goal to `all` because this file defines some targets
# before the inclusion of erlang.mk leading to the wrong target becoming
# the default.
.DEFAULT_GOAL = all
endif
# Automatically add rabbitmq-common to the dependencies, at least for
# the Makefiles.
ifneq ($(PROJECT),rabbit_common)
ifneq ($(PROJECT),rabbitmq_public_umbrella)
ifeq ($(filter rabbit_common,$(DEPS)),)
DEPS += rabbit_common
endif
endif
endif
# --------------------------------------------------------------------
# RabbitMQ components.
# --------------------------------------------------------------------
# For RabbitMQ repositories, we want to checkout branches which match
# the parent project. For instance, if the parent project is on a
# release tag, dependencies must be on the same release tag. If the
# parent project is on a topic branch, dependencies must be on the same
# topic branch or fallback to `stable` or `master` whichever was the
# base of the topic branch.
dep_amqp_client = git_rmq rabbitmq-erlang-client $(current_rmq_ref) $(base_rmq_ref) master
dep_rabbit = git_rmq rabbitmq-server $(current_rmq_ref) $(base_rmq_ref) master
dep_rabbit_common = git_rmq rabbitmq-common $(current_rmq_ref) $(base_rmq_ref) master
dep_rabbitmq_amqp1_0 = git_rmq rabbitmq-amqp1.0 $(current_rmq_ref) $(base_rmq_ref) master
dep_rabbitmq_auth_backend_amqp = git_rmq rabbitmq-auth-backend-amqp $(current_rmq_ref) $(base_rmq_ref) master
dep_rabbitmq_auth_backend_http = git_rmq rabbitmq-auth-backend-http $(current_rmq_ref) $(base_rmq_ref) master
dep_rabbitmq_auth_backend_ldap = git_rmq rabbitmq-auth-backend-ldap $(current_rmq_ref) $(base_rmq_ref) master
dep_rabbitmq_auth_mechanism_ssl = git_rmq rabbitmq-auth-mechanism-ssl $(current_rmq_ref) $(base_rmq_ref) master
dep_rabbitmq_boot_steps_visualiser = git_rmq rabbitmq-boot-steps-visualiser $(current_rmq_ref) $(base_rmq_ref) master
dep_rabbitmq_clusterer = git_rmq rabbitmq-clusterer $(current_rmq_ref) $(base_rmq_ref) master
dep_rabbitmq_codegen = git_rmq rabbitmq-codegen $(current_rmq_ref) $(base_rmq_ref) master
dep_rabbitmq_consistent_hash_exchange = git_rmq rabbitmq-consistent-hash-exchange $(current_rmq_ref) $(base_rmq_ref) master
dep_rabbitmq_delayed_message_exchange = git_rmq rabbitmq-delayed-message-exchange $(current_rmq_ref) $(base_rmq_ref) master
dep_rabbitmq_dotnet_client = git_rmq rabbitmq-dotnet-client $(current_rmq_ref) $(base_rmq_ref) master
dep_rabbitmq_event_exchange = git_rmq rabbitmq-event-exchange $(current_rmq_ref) $(base_rmq_ref) master
dep_rabbitmq_federation = git_rmq rabbitmq-federation $(current_rmq_ref) $(base_rmq_ref) master
dep_rabbitmq_federation_management = git_rmq rabbitmq-federation-management $(current_rmq_ref) $(base_rmq_ref) master
dep_rabbitmq_java_client = git_rmq rabbitmq-java-client $(current_rmq_ref) $(base_rmq_ref) master
dep_rabbitmq_lvc = git_rmq rabbitmq-lvc-plugin $(current_rmq_ref) $(base_rmq_ref) master
dep_rabbitmq_management = git_rmq rabbitmq-management $(current_rmq_ref) $(base_rmq_ref) master
dep_rabbitmq_management_agent = git_rmq rabbitmq-management-agent $(current_rmq_ref) $(base_rmq_ref) master
dep_rabbitmq_management_exchange = git_rmq rabbitmq-management-exchange $(current_rmq_ref) $(base_rmq_ref) master
dep_rabbitmq_management_themes = git_rmq rabbitmq-management-themes $(current_rmq_ref) $(base_rmq_ref) master
dep_rabbitmq_management_visualiser = git_rmq rabbitmq-management-visualiser $(current_rmq_ref) $(base_rmq_ref) master
dep_rabbitmq_message_timestamp = git_rmq rabbitmq-message-timestamp $(current_rmq_ref) $(base_rmq_ref) master
dep_rabbitmq_metronome = git_rmq rabbitmq-metronome $(current_rmq_ref) $(base_rmq_ref) master
dep_rabbitmq_mqtt = git_rmq rabbitmq-mqtt $(current_rmq_ref) $(base_rmq_ref) master
dep_rabbitmq_recent_history_exchange = git_rmq rabbitmq-recent-history-exchange $(current_rmq_ref) $(base_rmq_ref) master
dep_rabbitmq_rtopic_exchange = git_rmq rabbitmq-rtopic-exchange $(current_rmq_ref) $(base_rmq_ref) master
dep_rabbitmq_sharding = git_rmq rabbitmq-sharding $(current_rmq_ref) $(base_rmq_ref) master
dep_rabbitmq_shovel = git_rmq rabbitmq-shovel $(current_rmq_ref) $(base_rmq_ref) master
dep_rabbitmq_shovel_management = git_rmq rabbitmq-shovel-management $(current_rmq_ref) $(base_rmq_ref) master
dep_rabbitmq_stomp = git_rmq rabbitmq-stomp $(current_rmq_ref) $(base_rmq_ref) master
dep_rabbitmq_toke = git_rmq rabbitmq-toke $(current_rmq_ref) $(base_rmq_ref) master
dep_rabbitmq_top = git_rmq rabbitmq-top $(current_rmq_ref) $(base_rmq_ref) master
dep_rabbitmq_tracing = git_rmq rabbitmq-tracing $(current_rmq_ref) $(base_rmq_ref) master
dep_rabbitmq_test = git_rmq rabbitmq-test $(current_rmq_ref) $(base_rmq_ref) master
dep_rabbitmq_web_dispatch = git_rmq rabbitmq-web-dispatch $(current_rmq_ref) $(base_rmq_ref) master
dep_rabbitmq_web_stomp = git_rmq rabbitmq-web-stomp $(current_rmq_ref) $(base_rmq_ref) master
dep_rabbitmq_web_stomp_examples = git_rmq rabbitmq-web-stomp-examples $(current_rmq_ref) $(base_rmq_ref) master
dep_rabbitmq_web_mqtt = git_rmq rabbitmq-web-mqtt $(current_rmq_ref) $(base_rmq_ref) master
dep_rabbitmq_web_mqtt_examples = git_rmq rabbitmq-web-mqtt-examples $(current_rmq_ref) $(base_rmq_ref) master
dep_rabbitmq_website = git_rmq rabbitmq-website $(current_rmq_ref) $(base_rmq_ref) live master
dep_sockjs = git_rmq sockjs-erlang $(current_rmq_ref) $(base_rmq_ref) master
dep_toke = git_rmq toke $(current_rmq_ref) $(base_rmq_ref) master
dep_rabbitmq_public_umbrella = git_rmq rabbitmq-public-umbrella $(current_rmq_ref) $(base_rmq_ref) master
# FIXME: As of 2015-11-20, we depend on Ranch 1.2.1, but erlang.mk
# defaults to Ranch 1.1.0. All projects depending indirectly on Ranch
# needs to add "ranch" as a BUILD_DEPS. The list of projects needing
# this workaround are:
# o rabbitmq-web-stomp
dep_ranch = git https://github.com/ninenines/ranch 1.2.1
RABBITMQ_COMPONENTS = amqp_client \
rabbit \
rabbit_common \
rabbitmq_amqp1_0 \
rabbitmq_auth_backend_amqp \
rabbitmq_auth_backend_http \
rabbitmq_auth_backend_ldap \
rabbitmq_auth_mechanism_ssl \
rabbitmq_boot_steps_visualiser \
rabbitmq_clusterer \
rabbitmq_codegen \
rabbitmq_consistent_hash_exchange \
rabbitmq_delayed_message_exchange \
rabbitmq_dotnet_client \
rabbitmq_event_exchange \
rabbitmq_federation \
rabbitmq_federation_management \
rabbitmq_java_client \
rabbitmq_lvc \
rabbitmq_management \
rabbitmq_management_agent \
rabbitmq_management_exchange \
rabbitmq_management_themes \
rabbitmq_management_visualiser \
rabbitmq_message_timestamp \
rabbitmq_metronome \
rabbitmq_mqtt \
rabbitmq_recent_history_exchange \
rabbitmq_rtopic_exchange \
rabbitmq_sharding \
rabbitmq_shovel \
rabbitmq_shovel_management \
rabbitmq_stomp \
rabbitmq_test \
rabbitmq_toke \
rabbitmq_top \
rabbitmq_tracing \
rabbitmq_web_dispatch \
rabbitmq_web_mqtt \
rabbitmq_web_mqtt_examples \
rabbitmq_web_stomp \
rabbitmq_web_stomp_examples \
rabbitmq_website
# Several components have a custom erlang.mk/build.config, mainly
# to disable eunit. Therefore, we can't use the top-level project's
# erlang.mk copy.
NO_AUTOPATCH += $(RABBITMQ_COMPONENTS)
ifeq ($(origin current_rmq_ref),undefined)
ifneq ($(wildcard .git),)
current_rmq_ref := $(shell (\
ref=$$(git branch --list | awk '/^\* \(.*detached / {ref=$$0; sub(/.*detached [^ ]+ /, "", ref); sub(/\)$$/, "", ref); print ref; exit;} /^\* / {ref=$$0; sub(/^\* /, "", ref); print ref; exit}');\
if test "$$(git rev-parse --short HEAD)" != "$$ref"; then echo "$$ref"; fi))
else
current_rmq_ref := master
endif
endif
export current_rmq_ref
ifeq ($(origin base_rmq_ref),undefined)
ifneq ($(wildcard .git),)
base_rmq_ref := $(shell \
(git rev-parse --verify -q stable >/dev/null && \
git merge-base --is-ancestor $$(git merge-base master HEAD) stable && \
echo stable) || \
echo master)
else
base_rmq_ref := master
endif
endif
export base_rmq_ref
# Repository URL selection.
#
# First, we infer other components' location from the current project
# repository URL, if it's a Git repository:
# - We take the "origin" remote URL as the base
# - The current project name and repository name is replaced by the
# target's properties:
# eg. rabbitmq-common is replaced by rabbitmq-codegen
# eg. rabbit_common is replaced by rabbitmq_codegen
#
# If cloning from this computed location fails, we fallback to RabbitMQ
# upstream which is GitHub.
# Maccro to transform eg. "rabbit_common" to "rabbitmq-common".
rmq_cmp_repo_name = $(word 2,$(dep_$(1)))
# Upstream URL for the current project.
RABBITMQ_COMPONENT_REPO_NAME := $(call rmq_cmp_repo_name,$(PROJECT))
RABBITMQ_UPSTREAM_FETCH_URL ?= https://github.com/rabbitmq/$(RABBITMQ_COMPONENT_REPO_NAME).git
RABBITMQ_UPSTREAM_PUSH_URL ?= git@github.com:rabbitmq/$(RABBITMQ_COMPONENT_REPO_NAME).git
# Current URL for the current project. If this is not a Git clone,
# default to the upstream Git repository.
ifneq ($(wildcard .git),)
git_origin_fetch_url := $(shell git config remote.origin.url)
git_origin_push_url := $(shell git config remote.origin.pushurl || git config remote.origin.url)
RABBITMQ_CURRENT_FETCH_URL ?= $(git_origin_fetch_url)
RABBITMQ_CURRENT_PUSH_URL ?= $(git_origin_push_url)
else
RABBITMQ_CURRENT_FETCH_URL ?= $(RABBITMQ_UPSTREAM_FETCH_URL)
RABBITMQ_CURRENT_PUSH_URL ?= $(RABBITMQ_UPSTREAM_PUSH_URL)
endif
# Macro to replace the following pattern:
# 1. /foo.git -> /bar.git
# 2. /foo -> /bar
# 3. /foo/ -> /bar/
subst_repo_name = $(patsubst %/$(1)/%,%/$(2)/%,$(patsubst %/$(1),%/$(2),$(patsubst %/$(1).git,%/$(2).git,$(3))))
# Macro to replace both the project's name (eg. "rabbit_common") and
# repository name (eg. "rabbitmq-common") by the target's equivalent.
#
# This macro is kept on one line because we don't want whitespaces in
# the returned value, as it's used in $(dep_fetch_git_rmq) in a shell
# single-quoted string.
dep_rmq_repo = $(if $(dep_$(2)),$(call subst_repo_name,$(PROJECT),$(2),$(call subst_repo_name,$(RABBITMQ_COMPONENT_REPO_NAME),$(call rmq_cmp_repo_name,$(2)),$(1))),$(pkg_$(1)_repo))
dep_rmq_commits = $(if $(dep_$(1)), \
$(wordlist 3,$(words $(dep_$(1))),$(dep_$(1))), \
$(pkg_$(1)_commit))
define dep_fetch_git_rmq
fetch_url1='$(call dep_rmq_repo,$(RABBITMQ_CURRENT_FETCH_URL),$(1))'; \
fetch_url2='$(call dep_rmq_repo,$(RABBITMQ_UPSTREAM_FETCH_URL),$(1))'; \
if test "$$$$fetch_url1" != '$(RABBITMQ_CURRENT_FETCH_URL)' && \
git clone -q -n -- "$$$$fetch_url1" $(DEPS_DIR)/$(call dep_name,$(1)); then \
fetch_url="$$$$fetch_url1"; \
push_url='$(call dep_rmq_repo,$(RABBITMQ_CURRENT_PUSH_URL),$(1))'; \
elif git clone -q -n -- "$$$$fetch_url2" $(DEPS_DIR)/$(call dep_name,$(1)); then \
fetch_url="$$$$fetch_url2"; \
push_url='$(call dep_rmq_repo,$(RABBITMQ_UPSTREAM_PUSH_URL),$(1))'; \
fi; \
cd $(DEPS_DIR)/$(call dep_name,$(1)) && ( \
$(foreach ref,$(call dep_rmq_commits,$(1)), \
git checkout -q $(ref) >/dev/null 2>&1 || \
) \
(echo "error: no valid pathspec among: $(call dep_rmq_commits,$(1))" \
1>&2 && false) ) && \
(test "$$$$fetch_url" = "$$$$push_url" || \
git remote set-url --push origin "$$$$push_url")
endef
# --------------------------------------------------------------------
# Component distribution.
# --------------------------------------------------------------------
list-dist-deps::
@:
prepare-dist::
@:
# --------------------------------------------------------------------
# Run a RabbitMQ node (moved from rabbitmq-run.mk as a workaround).
# --------------------------------------------------------------------
# Add "rabbit" to the build dependencies when the user wants to start
# a broker or to the test dependencies when the user wants to test a
# project.
#
# NOTE: This should belong to rabbitmq-run.mk. Unfortunately, it is
# loaded *after* erlang.mk which is too late to add a dependency. That's
# why rabbitmq-components.mk knows the list of targets which start a
# broker and add "rabbit" to the dependencies in this case.
ifneq ($(PROJECT),rabbit)
ifeq ($(filter rabbit,$(DEPS) $(BUILD_DEPS)),)
RUN_RMQ_TARGETS = run-broker \
run-background-broker \
run-node \
run-background-node \
start-background-node
ifneq ($(filter $(RUN_RMQ_TARGETS),$(MAKECMDGOALS)),)
BUILD_DEPS += rabbit
endif
endif
ifeq ($(filter rabbit,$(DEPS) $(BUILD_DEPS) $(TEST_DEPS)),)
ifneq ($(filter check tests tests-with-broker test,$(MAKECMDGOALS)),)
TEST_DEPS += rabbit
endif
endif
endif
ifeq ($(filter rabbit_public_umbrella amqp_client rabbit_common rabbitmq_test,$(PROJECT)),)
ifeq ($(filter rabbitmq_test,$(DEPS) $(BUILD_DEPS) $(TEST_DEPS)),)
TEST_DEPS += rabbitmq_test
endif
endif
# --------------------------------------------------------------------
# rabbitmq-components.mk checks.
# --------------------------------------------------------------------
ifeq ($(PROJECT),rabbit_common)
else ifdef SKIP_RMQCOMP_CHECK
else ifeq ($(IS_DEP),1)
else ifneq ($(filter co up,$(MAKECMDGOALS)),)
else
# In all other cases, rabbitmq-components.mk must be in sync.
deps:: check-rabbitmq-components.mk
fetch-deps: check-rabbitmq-components.mk
endif
# If this project is under the Umbrella project, we override $(DEPS_DIR)
# to point to the Umbrella's one. We also disable `make distclean` so
# $(DEPS_DIR) is not accidentally removed.
ifneq ($(wildcard ../../UMBRELLA.md),)
UNDER_UMBRELLA = 1
else ifneq ($(wildcard UMBRELLA.md),)
UNDER_UMBRELLA = 1
endif
ifeq ($(UNDER_UMBRELLA),1)
ifneq ($(PROJECT),rabbitmq_public_umbrella)
DEPS_DIR ?= $(abspath ..)
distclean:: distclean-components
@:
distclean-components:
endif
ifneq ($(filter distclean distclean-deps,$(MAKECMDGOALS)),)
SKIP_DEPS = 1
endif
endif
UPSTREAM_RMQ_COMPONENTS_MK = $(DEPS_DIR)/rabbit_common/mk/rabbitmq-components.mk
check-rabbitmq-components.mk:
$(verbose) cmp -s rabbitmq-components.mk \
$(UPSTREAM_RMQ_COMPONENTS_MK) || \
(echo "error: rabbitmq-components.mk must be updated!" 1>&2; \
false)
ifeq ($(PROJECT),rabbit_common)
rabbitmq-components-mk:
@:
else
rabbitmq-components-mk:
$(gen_verbose) cp -a $(UPSTREAM_RMQ_COMPONENTS_MK) .
ifeq ($(DO_COMMIT),yes)
$(verbose) git diff --quiet rabbitmq-components.mk \
|| git commit -m 'Update rabbitmq-components.mk' rabbitmq-components.mk
endif
endif

View File

@ -0,0 +1,82 @@
%% The contents of this file are subject to the Mozilla Public License
%% Version 1.1 (the "License"); you may not use this file except in
%% compliance with the License. You may obtain a copy of the License
%% at http://www.mozilla.org/MPL/
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and
%% limitations under the License.
%%
%% The Original Code is RabbitMQ.
%%
%% The Initial Developer of the Original Code is GoPivotal, Inc.
%% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
%%
-module(rabbit_auth_backend_cache).
-include_lib("rabbit_common/include/rabbit.hrl").
-behaviour(rabbit_authn_backend).
-behaviour(rabbit_authz_backend).
-export([user_login_authentication/2, user_login_authorization/1,
check_vhost_access/3, check_resource_access/3]).
%% Implementation of rabbit_auth_backend
user_login_authentication(Username, AuthProps) ->
with_cache({user_login_authentication, [Username, AuthProps]},
fun({ok, _}) -> success;
({refused, _}) -> refusal;
({error, _} = Err) -> Err
end).
user_login_authorization(Username) ->
with_cache({user_login_authorization, [Username]},
fun({ok, _}) -> success;
({ok, _, _}) -> success;
({refused, _}) -> refusal;
({error, _} = Err) -> Err
end).
check_vhost_access(#auth_user{} = AuthUser, VHostPath, Sock) ->
with_cache({check_vhost_access, [AuthUser, VHostPath, Sock]},
fun(true) -> success;
(false) -> refusal;
({error, _} = Err) -> Err
end).
check_resource_access(#auth_user{} = AuthUser,
#resource{} = Resource, Permission) ->
with_cache({check_resource_access, [AuthUser, Resource, Permission]},
fun(true) -> success;
(false) -> refusal;
({error, _} = Err) -> Err
end).
with_cache({F, A}, Fun) ->
{ok, AuthCache} = application:get_env(rabbitmq_auth_backend_cache,
cache_module),
case AuthCache:get({F, A}) of
{ok, Result} ->
Result;
{error, not_found} ->
{ok, Backend} = application:get_env(rabbitmq_auth_backend_cache,
cached_backend),
BackendResult = apply(Backend, F, A),
case should_cache(BackendResult, Fun) of
true -> ok = AuthCache:put({F, A}, BackendResult);
false -> ok
end,
BackendResult
end.
should_cache(Result, Fun) ->
{ok, CacheRefusals} = application:get_env(rabbitmq_auth_backend_cache,
cache_refusals),
case {Fun(Result), CacheRefusals} of
{success, _} -> true;
{refusal, true} -> true;
_ -> false
end.

View File

@ -0,0 +1,37 @@
%% The contents of this file are subject to the Mozilla Public License
%% Version 1.1 (the "License"); you may not use this file except in
%% compliance with the License. You may obtain a copy of the License
%% at http://www.mozilla.org/MPL/
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and
%% limitations under the License.
%%
%% The Original Code is RabbitMQ HTTP authentication.
%%
%% The Initial Developer of the Original Code is VMware, Inc.
%% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
%%
-module(rabbit_auth_backend_cache_app).
-behaviour(application).
-export([start/2, stop/1]).
-behaviour(supervisor).
-export([init/1]).
start(_Type, _StartArgs) ->
supervisor:start_link({local,?MODULE},?MODULE,[]).
stop(_State) ->
ok.
%%----------------------------------------------------------------------------
init([]) ->
{ok, AuthCache} = application:get_env(rabbitmq_auth_backend_cache,
cache_module),
{ok, {{one_for_one,3,10},[{auth_cache, {AuthCache, start_link, []},
permanent, 5000, worker, [AuthCache]}]}}.

View File

@ -0,0 +1,71 @@
%% The contents of this file are subject to the Mozilla Public License
%% Version 1.1 (the "License"); you may not use this file except in
%% compliance with the License. You may obtain a copy of the License
%% at http://www.mozilla.org/MPL/
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and
%% limitations under the License.
%%
%% The Original Code is RabbitMQ.
%%
%% The Initial Developer of the Original Code is GoPivotal, Inc.
%% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
%%
-module(rabbit_auth_cache_dict).
-behaviour(gen_server2).
-compile({no_auto_import,[get/1]}).
-compile({no_auto_import,[put/2]}).
-export([start_link/0,
get/1, put/2, delete/1]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-record(state, {ttl}).
start_link() -> gen_server2:start_link({local, ?MODULE}, ?MODULE, [], []).
get(Key) -> gen_server2:call(?MODULE, {get, Key}).
put(Key, Value) -> gen_server2:cast(?MODULE, {put, Key, Value}).
delete(Key) -> gen_server2:call(?MODULE, {delete, Key}).
init(_Args) ->
{ok, TTL} = application:get_env(rabbitmq_auth_backend_cache, cache_ttl),
{ok, #state{ttl = TTL}}.
handle_call({get, Key}, _From, State = #state{}) ->
Result = case erlang:get({item, Key}) of
{ok, Val} -> {ok, Val};
error -> {error, not_found}
end,
{reply, Result, State};
handle_call({delete, Key}, _From, State = #state{}) ->
do_delete(Key),
{reply, ok, State}.
handle_cast({put, Key, Value}, State = #state{ttl = TTL}) ->
erlang:put({items, Key}, Value),
{ok, TRef} = timer:apply_after(TTL, auth_cache_dict, delete, Key),
put({timers, Key}, TRef),
{noreply, State}.
handle_info(_Msg, State) ->
{noreply, State}.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
terminate(_Reason, State = #state{}) ->
State.
do_delete(Key) ->
erase({items, Key}),
case get({timers, Key}) of
{ok, Tref} -> erlang:cancel_timer(Tref),
erase({timers, Key});
error -> ok
end.

View File

@ -0,0 +1,86 @@
%% The contents of this file are subject to the Mozilla Public License
%% Version 1.1 (the "License"); you may not use this file except in
%% compliance with the License. You may obtain a copy of the License
%% at http://www.mozilla.org/MPL/
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and
%% limitations under the License.
%%
%% The Original Code is RabbitMQ.
%%
%% The Initial Developer of the Original Code is GoPivotal, Inc.
%% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
%%
-module(rabbit_auth_cache_ets).
-behaviour(gen_server2).
-compile({no_auto_import,[get/1]}).
-compile({no_auto_import,[put/2]}).
-compile(export_all).
-export([start_link/0,
get/1, put/2, delete/1]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-record(state, {cache, timers, ttl}).
start_link() -> gen_server2:start_link({local, ?MODULE}, ?MODULE, [], []).
get(Key) -> gen_server2:call(?MODULE, {get, Key}).
put(Key, Value) -> gen_server2:cast(?MODULE, {put, Key, Value}).
delete(Key) -> gen_server2:call(?MODULE, {delete, Key}).
init(_Args) ->
{ok, TTL} = application:get_env(rabbitmq_auth_backend_cache, cache_ttl),
{ok, #state{ttl = TTL,
cache = ets:new(?MODULE, [set, private]),
timers = ets:new(auth_cache_ets_timers, [set, private])}}.
handle_call({get, Key}, _From, State = #state{cache = Table}) ->
Result = case ets:lookup(Table, Key) of
[{Key, {Exp, Val}}] -> case expired(Exp) of
true -> {error, not_found};
false -> {ok, Val}
end;
[] -> {error, not_found}
end,
{reply, Result, State};
handle_call({delete, Key}, _From, State = #state{cache = Table, timers = Timers}) ->
do_delete(Key, Table, Timers),
{reply, ok, State}.
handle_cast({put, Key, Value}, State = #state{cache = Table, ttl = TTL, timers = Timers}) ->
do_delete(Key, Table, Timers),
Expiration = expiration(TTL),
ets:insert(Table, {Key, {Expiration, Value}}),
{ok, TRef} = timer:apply_after(TTL, auth_cache_dict, delete, Key),
ets:insert(Timers, {Key, TRef}),
{noreply, State}.
handle_info(_Msg, State) ->
{noreply, State}.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
terminate(_Reason, State = #state{}) ->
State.
do_delete(Key, Table, Timers) ->
true = ets:delete(Table, Key),
case ets:lookup(Timers, Key) of
[{Key, Tref}] -> erlang:cancel_timer(Tref),
true = ets:delete(Timers, Key);
[] -> ok
end.
expiration(TTL) ->
time_compat:erlang_system_time(milli_seconds) + TTL.
expired(Exp) ->
time_compat:erlang_system_time(milli_seconds) > Exp.

View File

@ -0,0 +1,118 @@
%% The contents of this file are subject to the Mozilla Public License
%% Version 1.1 (the "License"); you may not use this file except in
%% compliance with the License. You may obtain a copy of the License
%% at http://www.mozilla.org/MPL/
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and
%% limitations under the License.
%%
%% The Original Code is RabbitMQ.
%%
%% The Initial Developer of the Original Code is GoPivotal, Inc.
%% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
%%
-module(rabbit_auth_cache_ets_segmented).
-behaviour(gen_server2).
-export([start_link/0,
get/1, put/2, delete/1]).
-export([gc/0]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-record(state, {boundary, old_segment, current_segment, garbage, gc_timer}).
start_link() -> gen_server2:start_link({local, ?MODULE}, ?MODULE, [], []).
get(Key) ->
case get_from_segments(Key) of
[] -> {error, not_found};
[V|_] -> {ok, V}
end.
put(Key, Value) ->
TTL = cache_ttl(),
Expiration = expiration(TTL),
Segment = gen_server2:call(?MODULE, {get_write_segment, Expiration}),
ets:insert(Segment, {Key, {Expiration, Value}}),
ok.
delete(Key) ->
[ets:delete(Table, Key) || Table <- gen_server2:call(?MODULE, get_segments)].
gc() ->
case whereis(?MODULE) of
undefined -> ok;
Pid -> Pid ! gc
end.
init(_Args) ->
Segment = ets:new(segment, [set, protected]),
SegmentSize = segment_size(),
Boundary = expiration(SegmentSize),
{ok, Timer} = timer:send_interval(SegmentSize * 2, gc),
{ok, #state{boundary = Boundary, current_segment = Segment, garbage = [], gc_timer = Timer}}.
handle_call({get_write_segment, Expiration}, _From,
State = #state{boundary = Boundary,
current_segment = CurrentSegment,
old_segment = OldSegment,
garbage = Garbage}) when Boundary =< Expiration ->
NewSegment = ets:new(segment, [set, protected]),
NewBoundary = Boundary + segment_size(),
{reply, NewSegment, State#state{boundary = NewBoundary,
current_segment = NewSegment,
old_segment = CurrentSegment,
garbage = [OldSegment | Garbage]}};
handle_call({get_write_segment, _}, _From, State = #state{current_segment = CurrentSegment}) ->
{reply, CurrentSegment, State};
handle_call(get_segments, _From, State = #state{old_segment = Old, current_segment = Current}) ->
{reply, [Current, Old], State}.
handle_cast(_, State = #state{}) ->
{noreply, State}.
handle_info(gc, State = #state{ garbage = Garbage }) ->
[ets:delete(Segment) || Segment <- Garbage, Segment =/= undefined],
{noreply, State#state{ garbage = [] }};
handle_info(_Msg, State) ->
{noreply, State}.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
terminate(_Reason, State = #state{gc_timer = Timer}) ->
erlang:cancel_timer(Timer),
State.
segment_size() ->
cache_ttl() * 2.
cache_ttl() ->
application:get_env(rabbitmq_auth_backend_cache, cache_ttl).
expiration(TTL) ->
time_compat:erlang_system_time(milliseconds) + TTL.
expired(Exp) ->
time_compat:erlang_system_time(milliseconds) > Exp.
get_from_segments(Key) ->
Segments = gen_server2:call(?MODULE, get_segments),
lists:flatmap(
fun(T) ->
case ets:lookup(T, Key) of
[{Key, {Exp, Val}}] ->
case expired(Exp) of
true -> [];
false -> [Val]
end;
[] -> []
end
end,
Segments).

View File

@ -0,0 +1,12 @@
%% -*- erlang -*-
{application, rabbitmq_auth_backend_cache,
[{description, "RabbitMQ Authentication Backend cache"},
{vsn, ""},
{modules, [rabbit_auth_backend_cache_app]},
{registered, []},
{mod, {rabbit_auth_backend_cache_app, []}},
{env, [{cache_ttl, 15000},
{cache_module, rabbit_auth_cache_ets},
{cached_backend, rabbit_auth_backend_internal},
{cache_refusals, false}] },
{applications, [kernel, stdlib, rabbit_common]}]}.