Packages
oidcc
1.3.0
3.7.2
3.7.1
3.7.0
3.6.0
3.5.2
3.5.1
3.5.0
retired
3.4.0
3.3.0
3.2.6
3.2.5
3.2.4
3.2.3
3.2.2
3.2.1
3.2.0
3.2.0-beta.3
3.2.0-beta.2
retired
3.2.0-beta.1
retired
3.1.2
3.1.2-beta.1
retired
3.1.1
retired
3.1.0
retired
3.1.0-beta.2
retired
3.1.0-beta.1
retired
3.0.2
3.0.1
retired
3.0.0
retired
3.0.0-rc.6
retired
3.0.0-rc.5
retired
3.0.0-rc.4
retired
3.0.0-rc.3
retired
3.0.0-rc.2
retired
3.0.0-rc.1
retired
3.0.0-alpha.5
retired
3.0.0-alpha.4
retired
3.0.0-alpha.3
retired
3.0.0-alpha.2
retired
3.0.0-alpha.1
retired
2.0.0-alpha.2
retired
2.0.0-alpha.1
retired
1.8.1
retired
1.8.0
retired
1.7.0
retired
1.6.0
retired
1.5.1
retired
1.5.0
retired
1.4.0
retired
1.3.0
retired
1.2.1
retired
1.2.0
retired
1.1.0
retired
1.0.1
retired
1.0.0
retired
OpenID Connect client library for the BEAM.
Retired package: Deprecated - Unmaintained major version, update
Current section
Files
Jump to
Current section
Files
src/oidcc_http_cache.erl
-module(oidcc_http_cache).
-behaviour(gen_server).
%% API.
-export([start_link/0]).
-export([stop/0]).
-export([cache_http_result/3]).
-export([lookup_http_call/2]).
-export([enqueue_http_call/2]).
-export([trigger_cleaning/0]).
%% gen_server.
-export([init/1]).
-export([handle_call/3]).
-export([handle_cast/2]).
-export([handle_info/2]).
-export([terminate/2]).
-export([code_change/3]).
-record(state, {
ets_cache = undefined,
ets_time = undefined,
cache_duration = undefined,
clean_timeout = undefined,
last_clean = undefined
}).
%% API.
-spec start_link() -> {ok, pid()}.
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
-spec stop() -> ok.
stop() ->
gen_server:cast(?MODULE, stop).
cache_http_result(Method, Request, Result) ->
Key = {Method, Request},
gen_server:call(?MODULE, {cache_http, Key, Result}).
lookup_http_call(Method, Request) ->
Key = {Method, Request},
read_cache(Key).
enqueue_http_call(Method, Request) ->
Key = {Method, Request},
gen_server:call(?MODULE, {enqueue, Key}, 30000).
trigger_cleaning() ->
gen_server:cast(?MODULE, clean_cache).
-define(REQUEST_BUFFER, 30).
%% gen_server.
init(_) ->
EtsCache = ets:new(oidcc_ets_http_cache, [set, protected, named_table]),
EtsTime = ets:new(oidcc_ets_http_cache_time, [ordered_set, private]),
CacheDuration = application:get_env(oidcc, http_cache_duration, none),
CleanTimeout = application:get_env(oidcc, http_cache_clean, 60),
Now = erlang:system_time(seconds),
{ok, #state{ets_cache=EtsCache,
ets_time = EtsTime,
cache_duration = CacheDuration,
clean_timeout = CleanTimeout,
last_clean = Now
}}.
handle_call({enqueue, Key}, _From, State) ->
Result = insert_into_cache(Key, pending, State),
{reply, Result, State};
handle_call({cache_http, Key, Result}, _From, State) ->
ok = trigger_cleaning_if_needed(State),
ok = insert_into_cache(Key, Result, State),
{reply, ok, State};
handle_call(_Request, _From, State) ->
{reply, ignored, State}.
insert_into_cache(Key, Result, #state{ets_cache = EtsCache, ets_time = EtsTime,
cache_duration=Duration})
when is_integer(Duration), Duration > 0 ->
Now = erlang:system_time(seconds),
Timeout =
case Result of
pending ->
Now + oidcc_http_util:request_timeout(s) + ?REQUEST_BUFFER;
_ ->
Now + Duration
end,
Inserted = ets:insert_new(EtsCache, {Key, Timeout, Result}),
case {Result, Inserted} of
{pending, true} ->
true = ets:insert(EtsTime, {Timeout, Key}),
true;
{pending, false} ->
false;
{_, _} ->
true = ets:insert(EtsCache, {Key, Timeout, Result}),
true = ets:insert(EtsTime, {Timeout, Key}),
ok
end;
insert_into_cache(_Key, pending, _NoDuration) ->
%% if not using cache always perform the request
true;
insert_into_cache(_Key, _Result, _NoDuration) ->
ok.
handle_cast(clean_cache, #state{ets_cache=EtsCache,
ets_time=EtsTime
} = State) ->
Now = erlang:system_time(seconds),
case ets:first(EtsTime) of
'$end_of_table' ->
ok;
Timeout ->
delete_entry_if_outdated(Timeout, EtsCache, EtsTime, Now >= Timeout)
end,
{noreply, State#state{last_clean=Now}};
handle_cast(stop, State) ->
{stop, normal, State};
handle_cast(_Msg, State) ->
{noreply, State}.
handle_info(_Info, State) ->
{noreply, State}.
terminate(_Reason, _State) ->
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
read_cache(Key) ->
Now = erlang:system_time(seconds),
case ets:lookup(oidcc_ets_http_cache, Key) of
[{Key, Timeout, Result}] ->
return_if_not_outdated(Result, Timeout > Now);
[] ->
{error, not_found}
end.
trigger_cleaning_if_needed(#state{last_clean=LastClean,
clean_timeout=CleanTimeout}) ->
Now = erlang:system_time(seconds),
case (Now - LastClean) >= CleanTimeout of
true ->
trigger_cleaning(),
ok;
_ ->
ok
end.
return_if_not_outdated(Result, true) ->
{ok, Result};
return_if_not_outdated(_, _) ->
trigger_cleaning(),
{error, outdated}.
delete_entry_if_outdated(Timeout, EtsCache, EtsTime, true) ->
[{Timeout, Key}] = ets:lookup(EtsTime, Timeout),
true = ets:delete(EtsTime, Timeout),
true = ets:delete(EtsCache, Key),
trigger_cleaning(),
ok;
delete_entry_if_outdated(_Timeout, _EtsCache, _EtsTime, _) ->
ok.